Compare commits

...

36 Commits

Author SHA1 Message Date
tommy a94272ffd6 update branch to be in line with develop 2022-08-18 13:28:05 +02:00
Dave Hrycyszyn 773f9e5ead Moving helpful comment into docs comment 2022-08-18 10:45:42 +01:00
Pierre Dommerc 79f695f138 Wallet: new UI for pending delegation (#1499)
* refactor(wallet-delegation): new delegation pending ui

* refactor(wallet-delegation): remove pending delegation table

* refactor(wallet-delegation): new pending delegation ui

* refactor(wallet-delegation): remove logs

* refactor(wallet-delegation): better typing

* refactor(wallet-balance): feedback review
2022-08-18 11:20:32 +02:00
Pierre Dommerc 3aabbcf876 fix(nym-wallet): make get_operator_rewards request success optional (#1542) 2022-08-17 17:21:36 +02:00
Jędrzej Stuczyński d96b7408db Allowing optional fee for top-level NymdClient (#1541)
* Allowing optional fee for top-level NymdClient

* Updated changelog

* Fixed usages of said methods in validator-api
2022-08-17 13:27:53 +01:00
Jon Häggblad 728b0f4549 inclusion-probability: new crate for simulation active set inclusion (#1534) 2022-08-16 14:19:55 +02:00
tommy 0a37c81709 remove not needed prior state of denom 2022-08-16 12:05:38 +02:00
Jędrzej Stuczyński 957cbb45b0 Chore/linter updates (#1530)
* Removed needless return

* Added Eq derivation where applicable for types with PartialEq
2022-08-12 17:56:49 +01:00
Pierre Dommerc 8ec074cb1f fix(wallet): typo (#1527) 2022-08-12 16:02:27 +02:00
Jess ab5740087f Update CHANGELOG.md 2022-08-12 14:55:39 +01:00
Jess 6af59c303e Update CHANGELOG.md 2022-08-12 14:54:09 +01:00
Jess 27b384e034 Update CHANGELOG.md 2022-08-12 14:53:40 +01:00
Dave Hrycyszyn 7f5ce3ffeb Removing wallet entries from main changelog 2022-08-12 12:58:49 +01:00
Dave Hrycyszyn 89b6667c75 Splitting changelogs into their own files 2022-08-12 12:50:17 +01:00
Tommy Verrall a94a9aeaf5 Update README.md 2022-08-11 16:18:06 +01:00
tommy 6bc8b88a20 WIP - validator-api-tests
- Test base line
- Jest / Typescript / Node
2022-08-11 17:14:04 +02:00
Fouad 21f3991714 update sample env (#1523) 2022-08-11 12:21:19 +01:00
Tommy Verrall cd8eba988a Merge pull request #1522 from nymtech/release/wallet-v1.0.8
Release/wallet v1.0.8
2022-08-11 11:45:53 +01:00
Tommy Verrall d2b3841bbd Update Cargo.toml 2022-08-11 11:45:28 +01:00
Tommy Verrall de877fb337 Merge pull request #1521 from nymtech/release/nym-binaries-1.0.2
Release/nym binaries 1.0.2
2022-08-11 11:42:36 +01:00
Tommy Verrall d4c2b9060f Update changelog for v1.0.2 -reference 2022-08-11 10:02:34 +01:00
Bogdan-Ștefan Neacşu 41ac866729 Feature/validator api panics (#1520)
* Coconut unwraps

* Fail softly on epoch queries

* Update changelog

* Retry a few times before failing as before
2022-08-11 11:58:17 +03:00
Raphaël Walther a7afd2a1c7 Added audit workflow 2022-08-11 10:41:57 +02:00
Jon Häggblad df03daf2cc socks5: remove pub mod not needed anymore (#1517)
* socks5: remove pub mod not needed anymore

* all: dedup parse_validators and move to common

* rustfmt
2022-08-10 21:14:12 +02:00
Raphaël Walther b3f5a4f496 Added audit workflow 2022-08-10 16:35:49 +02:00
Bogdan-Ștefan Neacșu c7fdcf0a79 Change for default mainnet and fix typo 2022-08-10 13:40:28 +03:00
tommy 8edc762df9 Update
- update the cargo toml
- amend the work flow file
- and the envs to mainnet (disclaimer add qa after)
2022-08-10 11:10:38 +02:00
tommy 7b15f350cd add service-provider vesioning to match other binaries 2022-08-10 10:14:07 +02:00
tommy 2b4917b8b1 fix messaging on init for nym-client 2022-08-10 10:11:00 +02:00
tommy 921e558660 Update versions on binaries 2022-08-09 11:56:54 +02:00
tommy d62638b8e2 update wallet version 2022-08-08 16:53:00 +02:00
Benedetta 02da10e222 clean up 2022-08-04 15:37:05 +02:00
Benedetta 2637924e9c Fixing some to-do bits 2022-07-27 08:51:16 +02:00
Benedetta bbaa06417d Adding more tests 2022-07-25 08:39:07 +02:00
Benedetta edcaf72714 Creating the branch and initial work 2022-07-19 14:22:31 +02:00
Tommy 80ad7c1798 implement base changes for updating wallet-tests to use typescript 2022-07-14 17:51:48 +02:00
152 changed files with 7691 additions and 4915 deletions
-1
View File
@@ -31,7 +31,6 @@ jobs:
KEYBASE_NYMBOT_PAPERKEY: "${{ secrets.KEYBASE_NYMBOT_PAPERKEY }}"
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBTECH_TEAM }}"
KEYBASE_NYM_CHANNEL: "test"
IS_SUCCESS: "${{ env.WORKFLOW_CONCLUSION == 'success' }}"
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
+5 -5
View File
@@ -51,12 +51,12 @@ jobs:
- name: Install dependencies
run: yarn install
working-directory: nym-wallet/webdriver
working-directory: nym-wallet/wallet-ui-tests
- name: Remove existing user datafile
uses: JesseTG/rm@v1.0.2
with:
path: nym-wallet/webdriver/common/data/user-data.json
path: nym-wallet/wallet-ui-tests/common/user-data.json
- name: Create user data json file
id: create-json
@@ -64,7 +64,7 @@ jobs:
with:
name: "user-data.json"
json: ${{ secrets.WALLET_USERDATA }}
dir: "nym-wallet/webdriver/common/data/"
dir: "nym-wallet/wallet-ui-tests/common/"
- name: Install tauri-driver
uses: actions-rs/cargo@v1
@@ -73,5 +73,5 @@ jobs:
args: tauri-driver
- name: Launch tests
run: xvfb-run yarn test:runall
working-directory: nym-wallet/webdriver
run: xvfb-run yarn test
working-directory: nym-wallet/wallet-ui-tests
+12 -254
View File
@@ -2,8 +2,16 @@
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
## [Unreleased]
### Changed
- validator-client: made `fee` argument optional for `execute` and `execute_multiple` ([#1541])
[#1541]: https://github.com/nymtech/nym/pull/1541
## [nym-binaries-1.0.2](https://github.com/nymtech/nym/tree/nym-binaries-1.0.2)
### Added
@@ -25,6 +33,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
- validator-client: add `denom` argument and add simple test for querying an account balance
- gateway, validator-api: Checks for coconut credential double spending attempts, taking the coconut bandwidth contract as source of truth ([#1457])
- coconut-bandwidth-contract: Record the state of a coconut credential; create specific proposal for releasing funds ([#1457])
- inclusion-probability: add simulator for active set inclusion probability
### Fixed
@@ -53,6 +62,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
- 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
@@ -81,90 +91,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
[#1487]: https://github.com/nymtech/nym/pull/1487
[#1496]: https://github.com/nymtech/nym/pull/1496
[#1503]: https://github.com/nymtech/nym/pull/1503
## [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])
[#1520]: https://github.com/nymtech/nym/pull/1520
## [v1.0.1](https://github.com/nymtech/nym/tree/v1.0.1) (2022-05-04)
@@ -197,77 +124,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)
@@ -346,108 +206,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
+531 -477
View File
File diff suppressed because it is too large Load Diff
+9 -7
View File
@@ -22,22 +22,23 @@ members = [
"clients/native",
"clients/native/websocket-requests",
"clients/socks5",
"common/bandwidth-claim-contract",
"common/client-libs/gateway-client",
"common/client-libs/mixnet-client",
"common/client-libs/validator-client",
"common/credential-storage",
"common/coconut-interface",
"common/config",
"common/credentials",
"common/crypto",
"common/crypto/dkg",
"common/execute",
"common/bandwidth-claim-contract",
"common/cosmwasm-smart-contracts/coconut-bandwidth-contract",
"common/cosmwasm-smart-contracts/contracts-common",
"common/cosmwasm-smart-contracts/mixnet-contract",
"common/cosmwasm-smart-contracts/multisig-contract",
"common/cosmwasm-smart-contracts/vesting-contract",
"common/credential-storage",
"common/credentials",
"common/crypto",
"common/crypto/dkg",
"common/execute",
"common/inclusion-probability",
"common/mixnode-common",
"common/network-defaults",
"common/nonexhaustive-delayqueue",
@@ -53,9 +54,9 @@ members = [
"common/nymsphinx/params",
"common/nymsphinx/types",
"common/pemstore",
"common/statistics",
"common/socks5/proxy-helpers",
"common/socks5/requests",
"common/statistics",
"common/task",
"common/topology",
"common/types",
@@ -76,6 +77,7 @@ default-members = [
"clients/socks5",
"gateway",
"service-providers/network-requester",
"service-providers/network-statistics",
"mixnode",
"validator-api",
"explorer-api",
+1 -1
View File
@@ -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"
+2 -14
View File
@@ -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 {
+1 -1
View File
@@ -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 -1
View File
@@ -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"
+1 -12
View File
@@ -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
-5
View File
@@ -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;
@@ -893,7 +893,7 @@ impl<C> NymdClient<C> {
&self,
contract_address: &AccountId,
msg: &M,
fee: Fee,
fee: Option<Fee>,
memo: impl Into<String> + Send + 'static,
funds: Vec<Coin>,
) -> Result<ExecuteResult, NymdError>
@@ -901,6 +901,7 @@ impl<C> NymdClient<C> {
C: SigningCosmWasmClient + Sync,
M: ?Sized + Serialize + Sync,
{
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
self.client
.execute(self.address(), contract_address, msg, fee, memo, funds)
.await
@@ -910,7 +911,7 @@ impl<C> NymdClient<C> {
&self,
contract_address: &AccountId,
msgs: I,
fee: Fee,
fee: Option<Fee>,
memo: impl Into<String> + Send + 'static,
) -> Result<ExecuteResult, NymdError>
where
@@ -918,6 +919,7 @@ impl<C> NymdClient<C> {
I: IntoIterator<Item = (M, Vec<Coin>)> + Send,
M: Serialize,
{
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
self.client
.execute_multiple(self.address(), contract_address, msgs, fee, memo)
.await
+11
View File
@@ -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>,
+10
View File
@@ -0,0 +1,10 @@
[package]
name = "inclusion-probability"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rand = "0.8.5"
thiserror = "1.0.32"
@@ -0,0 +1,9 @@
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("The list of cumulative stake was unexpectedly empty")]
EmptyListCumulStake,
#[error("Sample point was unexpectedly out of bounds")]
SamplePointOutOfBounds,
#[error("Norm computation failed on different size arrarys")]
NormDifferenceSizeArrays,
}
+278
View File
@@ -0,0 +1,278 @@
//! Active set inclusion probability simulator
use error::Error;
mod error;
const TOLERANCE_L2_NORM: f64 = 1e-4;
const TOLERANCE_MAX_NORM: f64 = 1e-3;
pub struct SelectionProbability {
pub active_set_probability: Vec<f64>,
pub reserve_set_probability: Vec<f64>,
pub samples: u32,
pub delta_l2: f64,
pub delta_max: f64,
}
pub fn simulate_selection_probability_mixnodes(
list_stake_for_mixnodes: &[u64],
active_set_size: usize,
reserve_set_size: usize,
max_samples: u32,
) -> Result<SelectionProbability, Error> {
// Total number of existing (registered) nodes
let num_mixnodes = list_stake_for_mixnodes.len();
// Cumulative stake ordered by node index
let list_cumul = cumul_sum(list_stake_for_mixnodes);
// The computed probabilities
let mut active_set_probability = vec![0.0; num_mixnodes];
let mut reserve_set_probability = vec![0.0; num_mixnodes];
// Number sufficiently large to have a good approximation of selection probability
let mut samples = 0;
let mut delta_l2;
let mut delta_max;
let mut rng = rand::thread_rng();
loop {
samples += 1;
let mut sample_active_mixnodes = Vec::new();
let mut sample_reserve_mixnodes = Vec::new();
let mut list_cumul_temp = list_cumul.clone();
let active_set_probability_previous = active_set_probability.clone();
// Select the active nodes for the epoch (hour)
while sample_active_mixnodes.len() < active_set_size {
let candidate = sample_candidate(&list_cumul_temp, &mut rng)?;
if !sample_active_mixnodes.contains(&candidate) {
sample_active_mixnodes.push(candidate);
remove_mixnode_from_cumul_stake(candidate, &mut list_cumul_temp);
}
}
// Select the reserve nodes for the epoch (hour)
while sample_reserve_mixnodes.len() < reserve_set_size {
let candidate = sample_candidate(&list_cumul_temp, &mut rng)?;
if !sample_reserve_mixnodes.contains(&candidate)
&& !sample_active_mixnodes.contains(&candidate)
{
sample_reserve_mixnodes.push(candidate);
remove_mixnode_from_cumul_stake(candidate, &mut list_cumul_temp);
}
}
// Sum up nodes being in active or reserve set
for active_mixnodes in sample_active_mixnodes {
active_set_probability[active_mixnodes] += 1.0;
}
for reserve_mixnodes in sample_reserve_mixnodes {
reserve_set_probability[reserve_mixnodes] += 1.0;
}
// Convergence critera only on active set.
// We devide by samples to get the average, that is not really part of the delta
// computation.
delta_l2 = l2_diff(&active_set_probability, &active_set_probability_previous)?
/ f64::from(samples);
delta_max = max_diff(&active_set_probability, &active_set_probability_previous)?
/ f64::from(samples);
if samples > 10 && delta_l2 < TOLERANCE_L2_NORM && delta_max < TOLERANCE_MAX_NORM
|| samples >= max_samples
{
break;
}
}
active_set_probability
.iter_mut()
.for_each(|x| *x /= f64::from(samples));
reserve_set_probability
.iter_mut()
.for_each(|x| *x /= f64::from(samples));
Ok(SelectionProbability {
active_set_probability,
reserve_set_probability,
samples,
delta_l2,
delta_max,
})
}
// Compute the cumulative sum
fn cumul_sum<'a>(list: impl IntoIterator<Item = &'a u64>) -> Vec<u64> {
let mut list_cumul = Vec::new();
let mut cumul = 0;
for entry in list {
cumul += entry;
list_cumul.push(cumul);
}
list_cumul
}
fn sample_candidate(list_cumul: &[u64], rng: &mut rand::rngs::ThreadRng) -> Result<usize, Error> {
use rand::distributions::{Distribution, Uniform};
let uniform = Uniform::from(0..*list_cumul.last().ok_or(Error::EmptyListCumulStake)?);
let r = uniform.sample(rng);
let candidate = list_cumul
.iter()
.enumerate()
.find(|(_, x)| *x >= &r)
.ok_or(Error::SamplePointOutOfBounds)?
.0;
Ok(candidate)
}
// Update list of cumulative stake to reflect eliminating the picked node
fn remove_mixnode_from_cumul_stake(candidate: usize, list_cumul_stake: &mut [u64]) {
let prob_candidate = if candidate == 0 {
list_cumul_stake[0]
} else {
list_cumul_stake[candidate] - list_cumul_stake[candidate - 1]
};
for cumul in list_cumul_stake.iter_mut().skip(candidate) {
*cumul -= prob_candidate;
}
}
// Compute the difference in l2-norm
fn l2_diff(v1: &[f64], v2: &[f64]) -> Result<f64, Error> {
if v1.len() != v2.len() {
return Err(Error::NormDifferenceSizeArrays);
}
Ok(v1
.iter()
.zip(v2)
.map(|(&i1, &i2)| (i1 - i2).powi(2))
.sum::<f64>()
.sqrt())
}
// Compute the difference in max-norm
fn max_diff(v1: &[f64], v2: &[f64]) -> Result<f64, Error> {
if v1.len() != v2.len() {
return Err(Error::NormDifferenceSizeArrays);
}
Ok(v1
.iter()
.zip(v2)
.map(|(x, y)| (x - y).abs())
.fold(f64::NEG_INFINITY, f64::max))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn compute_cumul_sum() {
let v = cumul_sum(&vec![1, 2, 3]);
assert_eq!(v, &[1, 3, 6]);
}
#[test]
fn remove_mixnode_from_cumul() {
let mut cumul_stake = vec![1, 2, 3, 4, 5, 6];
remove_mixnode_from_cumul_stake(3, &mut cumul_stake);
assert_eq!(cumul_stake, &[1, 2, 3, 3, 4, 5]);
}
#[test]
fn max_norm() {
let v1 = vec![1.0, 2.0, 3.0];
let v2 = vec![2.0, 4.0, -6.0];
assert!((max_diff(&v1, &v2).unwrap() - 9.0).abs() < f64::EPSILON);
}
#[test]
fn ls_norm() {
let v1 = vec![1.0, 2.0, 3.0];
let v2 = vec![2.0, 3.0, -2.0];
assert!((l2_diff(&v1, &v2).unwrap() - 5.196_152_422_706_632).abs() < 1e2 * f64::EPSILON);
}
// Replicate the results from the Python simulation code in https://github.com/nymtech/team-core/issues/114
#[test]
fn replicate_python_simulation() {
let active_set_size = 4;
let standby_set_size = 1;
// this has to contain the total stake per node
let list_mix = vec![
100, 100, 3000, 500_000, 100, 10, 10, 10, 10, 10, 30000, 500, 200, 52345,
];
let max_samples = 100_000;
let SelectionProbability {
active_set_probability,
reserve_set_probability,
samples,
delta_l2,
delta_max,
} = simulate_selection_probability_mixnodes(
&list_mix,
active_set_size,
standby_set_size,
max_samples,
)
.unwrap();
// These values comes from running the python simulator for a very long time
let expected_active_set_probability = vec![
0.025_070_8,
0.025_073_2,
0.744_117,
0.999_999,
0.025_000_2,
0.002_524_4,
0.002_527_8,
0.002_528_6,
0.002_569_6,
0.002_513_6,
0.994,
0.125_482_8,
0.050_279_8,
0.998_313_2,
];
// The same check is used in the convergence criterion, and hence should be reflected in
// `delta_max` too.
assert!(
max_diff(&active_set_probability, &expected_active_set_probability).unwrap() < 1e-2
);
let expected_reserve_set_probability = vec![
0.076_392_4,
0.076_499,
0.204_893_6,
1e-06,
0.076_278_8,
0.007_720_6,
0.007_673,
0.007_700_2,
0.007_669_4,
0.007_731_2,
0.005_789_4,
0.368_465_6,
0.151_537_2,
0.001_648_6,
];
assert!(
max_diff(&reserve_set_probability, &expected_reserve_set_probability).unwrap() < 1e-2
);
// We converge around 20_000, add another 500 for some slack due to random values
assert!(samples < 20_500);
assert!(delta_l2 < TOLERANCE_L2_NORM);
assert!(delta_max < TOLERANCE_MAX_NORM);
}
}
+1 -1
View File
@@ -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> {
+32
View File
@@ -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
@@ -18,7 +18,6 @@ pub fn migrate_config_from_env(
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct OldContractState {
pub owner: Addr,
pub mix_denom: String,
pub rewarding_validator_address: Addr,
pub params: ContractStateParams,
}
+2 -3
View File
@@ -101,10 +101,9 @@ impl Account {
}
}
/// Returns the index of the next vesting period. Unless the current time is somehow in the past or vesting has not started yet.
/// In case vesting is over it will always return NUM_VESTING_PERIODS.
pub fn get_current_vesting_period(&self, block_time: Timestamp) -> Period {
// Returns the index of the next vesting period. Unless the current time is somehow in the past or vesting has not started yet.
// In case vesting is over it will always return NUM_VESTING_PERIODS.
if block_time.seconds() < self.periods.first().unwrap().start_time {
Period::Before
} else if self.periods.last().unwrap().end_time() < block_time {
+1 -1
View File
@@ -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 -1
View File
@@ -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"
+1 -12
View File
@@ -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
View File
@@ -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>",
+4 -13
View File
@@ -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 {
+14
View File
@@ -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
+1 -1
View File
@@ -3436,7 +3436,7 @@ dependencies = [
[[package]]
name = "nym-socks5-client"
version = "1.0.1"
version = "1.0.2"
dependencies = [
"clap",
"client-core",
+3 -2
View File
@@ -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
View File
@@ -1 +1 @@
ADMIN_ADDRESS={"MAINNET":[],"SANDBOX":[],"QA":["n1c8te4wlc25re97qw5nh8urtpek494zqx30lza2","n177krl498arsfupd2p9097kwfmuf07lkj6crmj9"]}
ADMIN_ADDRESS={\"MAINNET\":[],\"SANDBOX\":[],\"QA\":[\"n1c8te4wlc25re97qw5nh8urtpek494zqx30lza2\",\"n177krl498arsfupd2p9097kwfmuf07lkj6crmj9\"]}
+213
View File
@@ -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))
+1 -1
View File
@@ -3055,7 +3055,7 @@ dependencies = [
[[package]]
name = "nym_wallet"
version = "1.0.7"
version = "1.0.8"
dependencies = [
"aes-gcm",
"argon2 0.3.4",
+1 -1
View File
@@ -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 = ""
@@ -254,7 +254,7 @@ pub async fn get_mix_node_description(
host: &str,
port: u16,
) -> Result<NodeDescription, BackendError> {
return fetch_mix_node_description(host, port)
fetch_mix_node_description(host, port)
.await
.map_err(|e| BackendError::ReqwestError { source: e });
.map_err(|e| BackendError::ReqwestError { source: e })
}
@@ -1,13 +0,0 @@
{
"version": 1,
"accounts": [
{
"id": "default",
"account": {
"ciphertext": "cq5w4W5ex5eFFRcqLG+824XyUAUoYmrRY3NGw/rue6/mLoQKQE/07+BxzRuKjyYFasC1HBPg41KJwp2IY+/7+80rB9aXPpaKVLUcG9U40qgCw66WhgxTrXOnrt5toefpSTBL7f9N/PVwpuumfAgD9CS0ioB7/9Qoea7nYKkextGX15ex26B/ndQddvUkQ4gx+Vq7OLymv4l+nkdZ2nKMja349zd/BjnzPBB68/iIjyYlivVjtQ7FRbvpNRj6Mjg4905wGlO7bTpkw+RGiaGK4pK8fTWz8gAKr8GYoXPD",
"salt": "SPVGdbVyoEayD4ZzM4I+Jg==",
"iv": "tjpn/tRjD1gty+fQ"
}
}
]
}
@@ -1,13 +0,0 @@
{
"version": 1,
"accounts": [
{
"id": "default",
"account": {
"ciphertext": "3MDgoU2i5QMc9r80yPeq2AMk5wpkke0tXum5NsOE5NcFciF+aHLQW0dvXbGszap1y3nN4+YZD3cgGrmtKh/cChqRGJDkniaxdf3XPHh9RkiWXw2KSHHeyGrFY0INJeiky1ZtUFhWhopcHJWSnfCmVC15YFpnM5xOKpITjHAFhGt98MaYR+mS+3zoUFrjYbaZRh2TR2lFWsbR8YU1uaTYqJZ1HX1PBCub6aS3vjQm0Fwa+hAtR/gMymXJc5qtruTO4NbqYtMj3Z9eIgoVB+56SLAXlIF1Uo1pjvV0mx1hWNNiIc10ujF/wl/nnKF6icOcmrfm9XhOtsvUYBsE/wAIJZw3LKXgSX+hJbOl+zLAwJZK1xiL8n/nM1IJZDn+Wu6z0OzRaj9S7T16+brMw1oaqjk56saM8n5z725fizJj+ur6gnPBWnoyHPaCHgHdB2PKQNY0ZlwRM6dVncRaEWQDLAboyMq3FXxK9UbusNcDFpYw6bdnuJlNVf6y9yxwyvkUrt5YtgfkLyoW42z1PVtVWsV9P8eE/A/tnYjXf34xvba3K8Y1/3DTi7uuydNrSR/XhA+pevz68VWCbY+j746Yi8Lz7altePphkjfJAezodobKvMplXzqInopIWNovyemw/+1E7WZbkQIOAXg1WC1+Y/df+dffRGuGRdDerfRLmA5XLej1M/wE3WQ7b9KwlAo6XJ4hnQKwyDCqYP/ButBXW1AOnnZpCq59gGbiccZJsTMZB4OP95yFPgz8//IeDgma2PDixVmDEp0SGHhN7dlSoNa5eoglblqzJu/TcTA6jmQFA3ef0GiA3QzBjmyB4bz0bFybh8XA1brVIVlsjRwXb3/UYaVqsP6Hy1QDUpZofXIJs5lK0hUd0ECdaNFXXgHd25ifPocp09WLFyK92H6i3ABDZ7pu3b4lTUt6kHt6LTVsKkyylmYf2iMHnCcmfy4uxGTXxRjPjMgKL8pd++OZ3q62jLBuoTjgdj6pccwDvD+NYQ2FFeHmBzxyTLqUyKltYiyFlJHWLKOcXyeDHzRhHic+e/wn3VhM3NdrvtqYWA9m72Ye1L1I7VX7KatGurG6CeiFiY5xHxxpLT7dF0fJ7uxRye4JnRyYQuU7iK72qCKjgYjwjCIha4qPi5Q/x6S+uVe7yX5Eb73L3eB+IlkyW9wPHmSOcE4GpbMU96tK8xoxT0T9eQlj050GDnJ/oI2XHfZTs1bIxsjfZqW03g==",
"salt": "wXR3RnPmsoA3ncrixIvaUw==",
"iv": "/Zjn1OXsLJhA43n/"
}
}
]
}
@@ -1,21 +0,0 @@
{
"version": 1,
"accounts": [
{
"id": "first",
"account": {
"ciphertext": "icnpxLmr/H7FIIOaEf7DYNLuM6uhh7poEppXpYCllQD33TjY+8eLtVvhEQmjX60IQeFOd+1JCcrHa2B12vlBAYlfM4gBxA6d2ZJ8+Dw/vNvBNyChiyUx2euV3vPGOs22r/XDBsmEeF40XZcXftQZa2kzYaPnkbP+eiMOIWkcY4FYOEHwx5SxT4VBPZIrVTC3iDalJLWybVbbw/Bc2zbzEXI1ckg4Ccydj95SMil9BiyDpALfZqwlai7I97S+BjmcVxSCsYqFjTkRUHVMjrEr7fWHKU4DIOM=",
"salt": "CtnbfkxTybqz0U4cPHW2jQ==",
"iv": "77ZROU6dAMttEWwS"
}
},
{
"id": "second",
"account": {
"ciphertext": "nsqZHdQFlskglc5izKgnr8sBwdMmd82h2Rnjdos9EUca3cqkUdFYEjZDsK8OGR3GZ9alLTNt/1U97Rvvr2HPAWbzl23FW2YXaLTA6yj6ZwQK5w0MYE061NYbcxNHuzT9f5aQWkGULAk4RWb5t8eUX7y/NdJr3tA5xuGOLhooTfBB98/4RpupDsYGZp1DPC/GMFppOA3GmKs9bacZm805Bhfq5mwhXab1SjJQpFHMHisCMhxo/oLqulKML1tQMetBdqDTjJmPpdUnd1mi",
"salt": "J2TMLjKv4dkZ/kXso9FGhg==",
"iv": "CTqqoMa4LetvBKCP"
}
}
]
}
+1 -1
View File
@@ -1,7 +1,7 @@
{
"package": {
"productName": "nym-wallet",
"version": "1.0.7"
"version": "1.0.8"
},
"build": {
"distDir": "../dist",
@@ -75,7 +75,7 @@ export const BondedMixnode = ({
id: 'pm-cell',
},
{
cell: `${operatorRewards.amount} ${operatorRewards.denom}`,
cell: operatorRewards ? `${operatorRewards.amount} ${operatorRewards.denom}` : '-',
id: 'operator-rewards-cell',
},
{
@@ -86,7 +86,7 @@ export const BondedMixnode = ({
cell: (
<BondedMixnodeActions
onActionSelect={onActionSelect}
disabledRedeemAndCompound={Number(mixnode.operatorRewards.amount) === 0}
disabledRedeemAndCompound={(operatorRewards && Number(operatorRewards.amount) === 0) || false}
/>
),
id: 'actions-cell',
@@ -43,7 +43,9 @@ export const CompoundRewardsModal = ({
>
<ModalListItem
label="Rewards to redeem"
value={`${node.operatorRewards.amount} ${node.operatorRewards.denom.toUpperCase()}`}
value={
node.operatorRewards ? `${node.operatorRewards.amount} ${node.operatorRewards.denom.toUpperCase()}` : '-'
}
divider
/>
<ModalFee fee={fee} isLoading={isFeeLoading} divider />
@@ -43,7 +43,9 @@ export const RedeemRewardsModal = ({
>
<ModalListItem
label="Rewards to redeem"
value={`${node.operatorRewards.amount} ${node.operatorRewards.denom.toUpperCase()}`}
value={
node.operatorRewards ? `${node.operatorRewards.amount} ${node.operatorRewards.denom.toUpperCase()}` : '-'
}
divider
/>
<ModalFee fee={fee} isLoading={isFeeLoading} divider />
@@ -61,7 +61,9 @@ export const UnbondModal = ({ node, onConfirm, onClose, onError }: Props) => {
{isMixnode(node) && (
<ModalListItem
label="Operator rewards"
value={`${node.operatorRewards.amount} ${node.operatorRewards.denom.toUpperCase()}`}
value={
node.operatorRewards ? `${node.operatorRewards.amount} ${node.operatorRewards.denom.toUpperCase()}` : '-'
}
divider
/>
)}
+2 -2
View File
@@ -28,7 +28,7 @@ type ClientAddressProps = {
showEntireAddress?: boolean;
};
export const ClientAddressDisplay: FC<ClientAddressProps & { address?: string }> = ({
export const ClientAddressDisplay: FC<ClientAddressProps & { address?: string } > = ({
withLabel,
withCopy,
showEntireAddress,
@@ -44,7 +44,7 @@ export const ClientAddressDisplay: FC<ClientAddressProps & { address?: string }>
)}
<AddressTooltip address={address} visible={!showEntireAddress}>
<Typography variant="body2" component="span" sx={{ mr: 1, color: 'text.primary', fontWeight: 400 }}>
<Typography data-testid="accountNumber" variant="body2" component="span" sx={{ mr: 1, color: 'text.primary', fontWeight: 400 }}>
{showEntireAddress ? address || '' : splice(6, address)}
</Typography>
</AddressTooltip>
@@ -37,6 +37,8 @@ export const CopyToClipboard = ({ text = '', iconButton }: { text?: string; icon
}}
>
{!copied ? <ContentCopy sx={{ fontSize: 14 }} /> : <Check color="success" sx={{ fontSize: 14 }} />}
{!copied ? <ContentCopy data-testid="copyIcon"
fontSize="small" /> : <Check color="success" />}
</IconButton>
</Tooltip>
);
@@ -98,7 +98,7 @@ export const DelegationsActionsMenu: React.FC<{
<ActionsMenuItem title="Undelegate" Icon={<Undelegate />} onClick={() => handleActionSelect('undelegate')} />
<ActionsMenuItem
title="Redeem"
description="Trasfer your rewards to your balance"
description="Transfer your rewards to your balance"
Icon={<Typography sx={{ pl: 1 }}>R</Typography>}
onClick={() => handleActionSelect('redeem')}
disabled={disableRedeemingRewards}
@@ -16,12 +16,13 @@ import {
import LockOutlinedIcon from '@mui/icons-material/LockOutlined';
import { visuallyHidden } from '@mui/utils';
import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
import { DelegationWithEverything } from '@nymproject/types';
import { DelegationEvent, DelegationWithEverything } from '@nymproject/types';
import { Link } from '@nymproject/react/link/Link';
import { format, formatDistanceToNow, parseISO } from 'date-fns';
import { styled } from '@mui/material/styles';
import { tableCellClasses } from '@mui/material/TableCell';
import { DelegationListItemActions, DelegationsActionsMenu } from './DelegationActions';
import { DelegationWithEvent, isPendingDelegation, TDelegations } from '../../context/delegations';
const StyledTooltipTableCell = styled(TableCell)(({ theme }) => ({
[`&.${tableCellClasses.head}`]: {
@@ -71,13 +72,30 @@ function descendingComparator<T>(a: T, b: T, orderBy: keyof T) {
return 0;
}
function sortPendingDelegation(a: DelegationWithEvent, b: DelegationWithEvent) {
if (isPendingDelegation(a) && isPendingDelegation(b)) return 0;
if (isPendingDelegation(b)) return -1;
if (isPendingDelegation(a)) return 1;
return 2;
}
function getComparator<Key extends keyof DelegationWithEverything>(
order: Order,
orderBy: Key,
): (a: DelegationWithEverything, b: DelegationWithEverything) => number {
): (a: DelegationWithEvent, b: DelegationWithEvent) => number {
return order === 'desc'
? (a, b) => descendingComparator(a, b, orderBy)
: (a, b) => -descendingComparator(a, b, orderBy);
? (a, b) => {
const pendingSort = sortPendingDelegation(a, b);
if (pendingSort === 2)
return descendingComparator(a as DelegationWithEverything, b as DelegationWithEverything, orderBy);
return pendingSort;
}
: (a, b) => {
const pendingSort = -sortPendingDelegation(a, b);
if (pendingSort === 2)
return -descendingComparator(a as DelegationWithEverything, b as DelegationWithEverything, orderBy);
return pendingSort;
};
}
const EnhancedTableHead: React.FC<EnhancedTableProps> = ({ order, orderBy, onRequestSort }) => {
@@ -119,7 +137,7 @@ const EnhancedTableHead: React.FC<EnhancedTableProps> = ({ order, orderBy, onReq
export const DelegationList: React.FC<{
isLoading?: boolean;
items?: DelegationWithEverything[];
items?: TDelegations;
onItemActionClick?: (item: DelegationWithEverything, action: DelegationListItemActions) => void;
explorerUrl: string;
}> = ({ isLoading, items, onItemActionClick, explorerUrl }) => {
@@ -132,6 +150,15 @@ export const DelegationList: React.FC<{
setOrderBy(property);
};
const getRewardValue = (item: DelegationWithEvent) => {
if (isPendingDelegation(item)) {
return '';
}
// eslint-disable-next-line @typescript-eslint/naming-convention
const { accumulated_rewards } = item;
return !accumulated_rewards ? '-' : `${accumulated_rewards.amount} ${accumulated_rewards.denom}`;
};
return (
<TableContainer>
<Table sx={{ width: '100%' }}>
@@ -149,12 +176,19 @@ export const DelegationList: React.FC<{
noIcon
/>
</TableCell>
<TableCell>{!item.avg_uptime_percent ? '-' : `${item.avg_uptime_percent}%`}</TableCell>
<TableCell>{!item.profit_margin_percent ? '-' : `${item.profit_margin_percent}%`}</TableCell>
<TableCell>
{!item.stake_saturation ? '-' : `${Math.round(item.stake_saturation * 100000) / 1000}%`}
{!isPendingDelegation(item) && (!item.avg_uptime_percent ? '-' : `${item.avg_uptime_percent}%`)}
</TableCell>
<TableCell>
{!isPendingDelegation(item) && (!item.profit_margin_percent ? '-' : `${item.profit_margin_percent}%`)}
</TableCell>
<TableCell>
{!isPendingDelegation(item) &&
(!item.stake_saturation ? '-' : `${Math.round(item.stake_saturation * 100000) / 1000}%`)}
</TableCell>
<TableCell>
{!isPendingDelegation(item) && format(new Date(item.delegated_on_iso_datetime), 'dd/MM/yyyy')}
</TableCell>
<TableCell>{format(new Date(item.delegated_on_iso_datetime), 'dd/MM/yyyy')}</TableCell>
<TableCell>
<Tooltip
placement="right"
@@ -169,55 +203,65 @@ export const DelegationList: React.FC<{
</TableRow>
</TableHead>
<TableBody>
{item.history.map((historyItem) => (
<TableRow key={`${historyItem.block_height}`}>
<StyledTooltipTableCell>
{formatDistanceToNow(parseISO(historyItem.delegated_on_iso_datetime), {
addSuffix: true,
})}
</StyledTooltipTableCell>
<StyledTooltipTableCell>
<Typography fontSize="inherit" noWrap>
{`${historyItem.amount.amount} ${historyItem.amount.denom}`}
{historyItem.uses_vesting_contract_tokens && (
<LockOutlinedIcon fontSize="inherit" sx={{ ml: 0.5 }} />
)}
</Typography>
</StyledTooltipTableCell>
<StyledTooltipTableCell>{historyItem.block_height}</StyledTooltipTableCell>
</TableRow>
))}
{!isPendingDelegation(item) &&
item.history.map((historyItem) => (
<TableRow key={`${historyItem.block_height}`}>
<StyledTooltipTableCell>
{formatDistanceToNow(parseISO(historyItem.delegated_on_iso_datetime), {
addSuffix: true,
})}
</StyledTooltipTableCell>
<StyledTooltipTableCell>
<Typography fontSize="inherit" noWrap>
{`${historyItem.amount.amount} ${historyItem.amount.denom}`}
{historyItem.uses_vesting_contract_tokens && (
<LockOutlinedIcon fontSize="inherit" sx={{ ml: 0.5 }} />
)}
</Typography>
</StyledTooltipTableCell>
<StyledTooltipTableCell>{historyItem.block_height}</StyledTooltipTableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
}
arrow
>
<span
style={{ cursor: 'pointer', textTransform: 'uppercase' }}
>{`${item.amount.amount} ${item.amount.denom}`}</span>
<span style={{ cursor: 'pointer', textTransform: 'uppercase' }}>
{!isPendingDelegation(item) && `${item.amount.amount} ${item.amount.denom}`}
</span>
</Tooltip>
</TableCell>
<TableCell sx={{ textTransform: 'uppercase' }}>
{!item.accumulated_rewards
? '-'
: `${item.accumulated_rewards.amount} ${item.accumulated_rewards.denom}`}
</TableCell>
<TableCell sx={{ textTransform: 'uppercase' }}>{getRewardValue(item)}</TableCell>
<TableCell align="right">
{!item.pending_events.length ? (
{!isPendingDelegation(item) && !item.pending_events.length && (
<DelegationsActionsMenu
isPending={undefined}
onActionClick={(action) => (onItemActionClick ? onItemActionClick(item, action) : undefined)}
disableRedeemingRewards={!item.accumulated_rewards || item.accumulated_rewards.amount === '0'}
disableCompoundRewards={!item.accumulated_rewards || item.accumulated_rewards.amount === '0'}
/>
) : (
)}
{!isPendingDelegation(item) && item.pending_events.length > 0 && (
<Tooltip
title="There will be a new epoch roughly every hour when your changes will take effect"
title="Your changes will take effect when
the new epoch starts. There is a new
epoch every hour."
arrow
>
<Chip label="Pending events" />
<Chip label="Pending Events" />
</Tooltip>
)}
{isPendingDelegation(item) && (
<Tooltip
title={`Your delegation of ${item.amount?.amount} ${item.amount?.denom} will take effect
when the new epoch starts. There is a new
epoch every hour.`}
arrow
>
<Chip label="Pending Events" />
</Tooltip>
)}
</TableCell>
@@ -1,160 +0,0 @@
import React, { FC } from 'react';
import LockOutlinedIcon from '@mui/icons-material/LockOutlined';
import {
Box,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
TableSortLabel,
Tooltip,
Typography,
} from '@mui/material';
import { CopyToClipboard } from '@nymproject/react/clipboard/CopyToClipboard';
import { DelegationEvent } from '@nymproject/types';
import { ArrowDropDown } from '@mui/icons-material';
import { visuallyHidden } from '@mui/utils';
import { Link } from '@nymproject/react/link/Link';
type Order = 'asc' | 'desc';
interface HeadCell {
id: keyof DelegationEvent;
label: string;
sortable: boolean;
disablePadding?: boolean;
}
interface EnhancedTableProps {
onRequestSort: (event: React.MouseEvent<unknown>, property: keyof DelegationEvent) => void;
order: Order;
orderBy: string;
}
const headCells: HeadCell[] = [
{ id: 'node_identity', label: 'Node ID', sortable: true },
{ id: 'amount', label: 'Delegation', sortable: true },
{ id: 'kind', label: 'Type', sortable: true },
];
function descendingComparator<T>(a: T, b: T, orderBy: keyof T) {
if (b[orderBy] < a[orderBy]) {
return -1;
}
if (b[orderBy] > a[orderBy]) {
return 1;
}
return 0;
}
function getComparator<Key extends keyof DelegationEvent>(
order: Order,
orderBy: Key,
): (a: DelegationEvent, b: DelegationEvent) => number {
return order === 'desc'
? (a, b) => descendingComparator(a, b, orderBy)
: (a, b) => -descendingComparator(a, b, orderBy);
}
const EnhancedTableHead: React.FC<EnhancedTableProps> = ({ order, orderBy, onRequestSort }) => {
const createSortHandler = (property: keyof DelegationEvent) => (event: React.MouseEvent<unknown>) => {
onRequestSort(event, property);
};
return (
<TableHead>
<TableRow>
{headCells.map((headCell) => (
<TableCell
key={headCell.id}
align="left"
padding={headCell.disablePadding ? 'none' : 'normal'}
sortDirection={orderBy === headCell.id ? order : false}
color="secondary"
>
<TableSortLabel
active={orderBy === headCell.id}
direction={orderBy === headCell.id ? order : 'asc'}
onClick={createSortHandler(headCell.id)}
IconComponent={ArrowDropDown}
>
{headCell.label}
{orderBy === headCell.id ? (
<Box component="span" sx={visuallyHidden}>
{order === 'desc' ? 'sorted descending' : 'sorted ascending'}
</Box>
) : null}
</TableSortLabel>
</TableCell>
))}
</TableRow>
</TableHead>
);
};
export const PendingEvents: FC<{ pendingEvents: DelegationEvent[]; explorerUrl: string }> = ({
pendingEvents,
explorerUrl,
}) => {
const [order, setOrder] = React.useState<Order>('asc');
const [orderBy, setOrderBy] = React.useState<keyof DelegationEvent>('node_identity');
const handleRequestSort = (event: React.MouseEvent<unknown>, property: keyof DelegationEvent) => {
const isAsc = orderBy === property && order === 'asc';
setOrder(isAsc ? 'desc' : 'asc');
setOrderBy(property);
};
if (pendingEvents.length === 0) return <Typography>No pending events</Typography>;
return (
<TableContainer>
<Table sx={{ width: '100%' }}>
<EnhancedTableHead order={order} orderBy={orderBy} onRequestSort={handleRequestSort} />
<TableBody>
{pendingEvents.sort(getComparator(order, orderBy)).map((item) => (
<TableRow key={`${item.node_identity}-${item.block_height}`}>
<TableCell>
<CopyToClipboard
sx={{ fontSize: 16, mr: 1 }}
value={item.node_identity}
tooltip={
<>
Copy identity key <strong>{item.node_identity}</strong> to clipboard
</>
}
/>
<Tooltip
title={
<>
Click to view <strong>{item.node_identity}</strong> in the Network Explorer
</>
}
placement="right"
arrow
>
<Link
target="_blank"
href={`${explorerUrl}/network-components/mixnode/${item.node_identity}`}
text={`${item.node_identity.slice(0, 6)}...${item.node_identity.slice(-6)}`}
/>
</Tooltip>
</TableCell>
<TableCell>{!item.amount ? '-' : `${item.amount?.amount} ${item.amount?.denom.toUpperCase()}`}</TableCell>
<TableCell>
{item.kind === 'Delegate' ? 'Delegation' : 'Undelegation'}
{item.proxy && (
<Tooltip title="Uses tokens for your vesting account" arrow>
<LockOutlinedIcon fontSize="inherit" sx={{ ml: 0.5 }} />
</Tooltip>
)}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
);
};
+2 -1
View File
@@ -18,10 +18,11 @@ export const Mnemonic = ({
Below is your 24 word mnemonic, make sure to store it in a safe place for accessing your wallet in the future
</Typography>
</Warning>
<TextField multiline rows={3} value={mnemonic} fullWidth />
<TextField multiline rows={3} value={mnemonic} fullWidth data-testid="mnemonicPhrase"/>
<Button
color="inherit"
data-testid="copyMnemonic"
disableElevation
size="large"
onClick={() => {
@@ -56,7 +56,7 @@ export const ConfirmationModal = ({
const ConfirmButton =
typeof confirmButton === 'string' ? (
<Button onClick={onConfirm} variant="contained" fullWidth disabled={disabled} sx={{ py: 1.6 }}>
<Typography variant="button" fontSize="large">
<Typography variant="button" fontSize="large" data-testid={confirmButton}>
{confirmButton}
</Typography>
</Button>
@@ -1,9 +1,6 @@
import React from 'react';
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;
@@ -16,7 +16,7 @@ const NetworkItem: React.FC<{ title: string; isSelected: boolean; onSelect: () =
isSelected,
onSelect,
}) => (
<ListItem button onClick={onSelect}>
<ListItem button onClick={onSelect} data-testid={title}>
<ListItemIcon>{isSelected && <CheckSharp color="success" />}</ListItemIcon>
<ListItemText>{title}</ListItemText>
</ListItem>
@@ -38,6 +38,7 @@ export const NetworkSelector = () => {
return (
<>
<Button
data-testid="networkEnv"
variant="text"
color="inherit"
sx={{ color: 'text.primary' }}
+1 -1
View File
@@ -31,7 +31,7 @@ export const NymCard: React.FC<{
{noPadding ? (
<CardContentNoPadding>{children}</CardContentNoPadding>
) : (
<CardContent sx={{ p: 3 }}>{children}</CardContent>
<CardContent sx={{ p: 3 }} >{children}</CardContent>
)}
</Card>
);
@@ -62,6 +62,9 @@ export const SendInputModal = ({
fullWidth
onChange={(e) => onAddressChange(e.target.value)}
value={toAddress}
inputProps={{
"data-testid": "recipientAddress",
}}
/>
<CurrencyFormField
placeholder="Amount"
@@ -73,7 +76,7 @@ export const SendInputModal = ({
initialValue={amount?.amount}
denom={denom}
/>
<Typography fontSize="smaller" sx={{ color: 'error.main' }}>
<Typography fontSize="smaller" sx={{ color: 'error.main' }} >
{error}
</Typography>
</Stack>
@@ -31,7 +31,7 @@ export const SendSuccessModal = ({
{txDetails && (
<>
<Typography variant="h5">{txDetails.amount}</Typography>
<Link href={txDetails.txUrl} target="_blank" sx={{ ml: 1 }} text="View on blockchain" />
<Link href={txDetails.txUrl} target="_blank" sx={{ ml: 1 }} text="View on blockchain" data-testid="viewOnBlockchain"/>
</>
)}
</Stack>
+6
View File
@@ -13,6 +13,9 @@ export const MnemonicInput: React.FC<{
<Stack spacing={2}>
<TextField
label="Mnemonic"
inputProps={{
"data-testid": "mnemonicInput",
}}
type={showPassword ? 'input' : 'password'}
value={mnemonic}
onChange={(e) => onUpdateMnemonic(e.target.value)}
@@ -63,6 +66,9 @@ export const PasswordInput: React.FC<{
</IconButton>
),
}}
inputProps={{
"data-testid": label,
}}
/>
</Box>
{error && <Error message={error} />}
+9 -18
View File
@@ -1,6 +1,6 @@
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 { isGateway, isMixnode, TBondGatewayArgs, TBondMixNodeArgs } from 'src/types';
import { Console } from 'src/utils/console';
import {
bondGateway as bondGatewayRequest,
@@ -28,19 +28,6 @@ import {
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;
@@ -48,13 +35,12 @@ export type TBondedMixnode = {
bond: DecCoin;
stakeSaturation: number;
profitMargin: number;
operatorRewards: DecCoin;
operatorRewards?: DecCoin;
delegators: number;
status: MixnodeStatus;
proxy?: string;
};
// TODO add relevant data
export interface TBondedGateway {
name: string;
identityKey: string;
@@ -173,7 +159,12 @@ export const BondingContextProvider = ({ children }: { children?: React.ReactNod
if (ownership.hasOwnership && ownership.nodeType === 'mixnode' && clientDetails) {
try {
const data = await getMixnodeBondDetails();
const operatorRewards = await getOperatorRewards(clientDetails?.client_address);
let operatorRewards;
try {
operatorRewards = await getOperatorRewards(clientDetails?.client_address);
} catch (e) {
console.warn(`get_operator_rewards request failed: ${e}`);
}
if (data) {
const { status, stakeSaturation, numberOfDelegators } = await getAdditionalMixnodeDetails(
data.mix_node.identity_key,
@@ -195,6 +186,7 @@ export const BondingContextProvider = ({ children }: { children?: React.ReactNod
} as TBondedMixnode);
}
} catch (e: any) {
console.warn(e);
setError(`While fetching current bond state, an error occurred: ${e}`);
}
}
@@ -211,7 +203,6 @@ export const BondingContextProvider = ({ children }: { children?: React.ReactNod
ip: data.gateway.host,
location: data.gateway.location,
bond: data.pledge_amount,
delegators: bonded.delegators,
proxy: data.proxy,
} as TBondedGateway);
}
+14 -3
View File
@@ -14,7 +14,7 @@ import { TPoolOption } from 'src/components';
export type TDelegationContext = {
isLoading: boolean;
error?: string;
delegations?: DelegationWithEverything[];
delegations?: TDelegations;
pendingDelegations?: DelegationEvent[];
totalDelegations?: string;
totalRewards?: string;
@@ -35,6 +35,12 @@ export type TDelegationTransaction = {
transactionUrl: string;
};
export type DelegationWithEvent = DelegationWithEverything | DelegationEvent;
export type TDelegations = DelegationWithEvent[];
export const isPendingDelegation = (delegation: DelegationWithEvent): delegation is DelegationEvent =>
'kind' in delegation;
export const DelegationContext = createContext<TDelegationContext>({
isLoading: true,
refresh: async () => undefined,
@@ -51,7 +57,7 @@ export const DelegationContextProvider: FC<{
}> = ({ network, children }) => {
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string>();
const [delegations, setDelegations] = useState<undefined | DelegationWithEverything[]>();
const [delegations, setDelegations] = useState<undefined | TDelegations>();
const [totalDelegations, setTotalDelegations] = useState<undefined | string>();
const [totalRewards, setTotalRewards] = useState<undefined | string>();
const [pendingDelegations, setPendingDelegations] = useState<DelegationEvent[]>();
@@ -87,8 +93,13 @@ export const DelegationContextProvider: FC<{
const data = await getDelegationSummary();
const pending = await getAllPendingDelegations();
const pendingOnNewNodes = pending.filter((event) => {
const some = data.delegations.some(({ node_identity }) => node_identity === event.node_identity);
return !some;
});
setPendingDelegations(pending);
setDelegations(data.delegations);
setDelegations([...data.delegations, ...pendingOnNewNodes]);
setTotalDelegations(`${data.total_delegations.amount} ${data.total_delegations.denom}`);
setTotalRewards(`${data.total_rewards.amount} ${data.total_rewards.denom}`);
} catch (e) {
+3 -3
View File
@@ -1,5 +1,5 @@
import React, { FC, useCallback, useEffect, useMemo, useState } from 'react';
import { TransactionExecuteResult } from '@nymproject/types';
import { DelegationWithEverything, TransactionExecuteResult } from '@nymproject/types';
import { RewardsContext, TRewardsTransaction } from '../rewards';
import { useDelegationContext } from '../delegations';
import { mockSleep } from './utils';
@@ -9,7 +9,7 @@ export const MockRewardsContextProvider: FC = ({ children }) => {
const [error, setError] = useState<string>();
const [totalRewards, setTotalRewards] = useState<undefined | string>();
const { delegations } = useDelegationContext();
const delegationsHash = delegations?.map((d) => d.accumulated_rewards).join(',');
const delegationsHash = delegations?.map((d) => (d as DelegationWithEverything).accumulated_rewards).join(',');
const resetState = () => {
setIsLoading(true);
@@ -19,7 +19,7 @@ export const MockRewardsContextProvider: FC = ({ children }) => {
const recalculate = () => {
const sum: number | undefined = delegations
?.map((d) => (d.accumulated_rewards ? Number(10) : Number(0)))
?.map((d) => ((d as DelegationWithEverything).accumulated_rewards ? Number(10) : Number(0)))
.reduce((acc, cur) => acc + cur, Number(0));
setTotalRewards(sum ? `${sum} NYM` : undefined);
@@ -6,7 +6,7 @@ export const Title = ({ title }: { title: string }) => (
);
export const Subtitle = ({ subtitle }: { subtitle: string }) => (
<Typography sx={{ color: 'common.white', textAlign: 'center', maxWidth: 450 }}>{subtitle}</Typography>
<Typography data-testid={subtitle} sx={{ color: 'common.white', textAlign: 'center', maxWidth: 450 }}>{subtitle}</Typography>
);
export const SubtitleSlick = ({ subtitle }: { subtitle: string }) => (
@@ -53,7 +53,8 @@ export const WordTiles = ({
return (
<Grid container spacing={3} justifyContent="center">
{mnemonicWords.map(({ name, index, disabled }) => (
<Grid item xs={2} key={index} onClick={() => onClick?.({ name, index })}>
<Grid
item xs={2} key={index} onClick={() => onClick?.({ name, index })} data-testid="mnemonicWordTile">
<WordTile
mnemonicWord={name}
index={showIndex ? index : undefined}
@@ -79,7 +80,7 @@ const HiddenWord = ({ mnemonicWord }: { mnemonicWord: THiddenMnemonicWord }) =>
</Box>
</Fade>
</Box>
<Typography>{mnemonicWord.index}.</Typography>
<Typography data-testid="wordIndex">{mnemonicWord.index}.</Typography>
</Stack>
);
@@ -20,6 +20,7 @@ export const ConfirmMnemonic = () => {
<Subtitle subtitle="Enter the mnemonic you wish to create a password for" />
<MnemonicInput mnemonic={localMnemonic} onUpdateMnemonic={(mnc) => setLocalMnemonic(mnc)} error={error} />
<Button
data-testid="nextToPasswordCreation"
size="large"
variant="contained"
fullWidth
@@ -37,6 +38,7 @@ export const ConfirmMnemonic = () => {
Next
</Button>
<Button
data-testid="backToMnemonicSignIn"
size="large"
color="inherit"
fullWidth
@@ -57,6 +57,7 @@ export const ConnectPassword = () => {
label="Confirm password"
/>
<Button
data-testid="createPasswordButton"
size="large"
variant="contained"
disabled={password !== confirmedPassword || password.length === 0 || !isStrongPassword || isLoading}
@@ -65,6 +66,7 @@ export const ConnectPassword = () => {
{isLoading ? <CircularProgress size={25} /> : 'Create password'}
</Button>
<Button
data-testid="backToStep1PasswordCreation"
size="large"
color="inherit"
onClick={() => {
@@ -23,6 +23,7 @@ export const CreateMnemonic = () => {
<Button
variant="contained"
data-testid="iSavedMnemonic"
color="primary"
disableElevation
size="large"
@@ -33,6 +34,7 @@ export const CreateMnemonic = () => {
I saved my mnemonic
</Button>
<Button
data-testid="backToWelcome"
onClick={() => {
resetState();
navigate(-1);
@@ -57,13 +57,14 @@ export const CreatePassword = () => {
/>
<Button
size="large"
data-testid="nextStorePassword"
variant="contained"
disabled={password !== confirmedPassword || password.length === 0 || !isStrongPassword || isLoading}
onClick={storePassword}
>
Next
</Button>
<Button size="large" color="info" onClick={handleSkip}>
<Button size="large" color="info" onClick={handleSkip} data-testid="skipPasswordAndSignInWithMnemonic">
Skip and sign in with mnemonic
</Button>
</Stack>
@@ -11,18 +11,18 @@ export const ExistingAccount = () => {
<Title title="Welcome to Nym" />
<SubtitleSlick subtitle="NEXT GENERATION OF PRIVACY" />
<Stack spacing={2} sx={{ width: 300 }}>
<Button variant="contained" size="large" onClick={() => navigate('/sign-in-mnemonic')} fullWidth>
<Button variant="contained" size="large" onClick={() => navigate('/sign-in-mnemonic')} fullWidth data-testid="signInWithMnemonic">
Sign in with mnemonic
</Button>
<Typography sx={{ textAlign: 'center', fontWeight: 600 }}>or</Typography>
<Button variant="contained" size="large" fullWidth onClick={() => navigate('/sign-in-password')}>
<Button variant="contained" size="large" fullWidth onClick={() => navigate('/sign-in-password')} data-testid="signInWithPassword">
Sign in with password
</Button>
<Box display="flex" justifyContent="space-between">
<Button color="inherit" onClick={() => navigate('/')}>
<Button color="inherit" onClick={() => navigate('/')} data-testid="backToWelcomePage">
Back
</Button>
<Button color="info" onClick={() => navigate('/forgot-password')}>
<Button color="info" onClick={() => navigate('/forgot-password')} data-testid="forgotPassword">
Forgot password?
</Button>
</Box>
@@ -39,15 +39,15 @@ export const SignInMnemonic = () => {
>
<Stack spacing={2}>
<MnemonicInput mnemonic={mnemonic} onUpdateMnemonic={(mnc) => setMnemonic(mnc)} error={error} />
<Button variant="contained" size="large" fullWidth type="submit">
<Button variant="contained" size="large" fullWidth type="submit" data-testid="signInSubmitButton">
Sign in with mnemonic
</Button>
<Box display="flex" justifyContent={passwordExists ? 'center' : 'space-between'}>
<Button color="inherit" onClick={() => handlePageChange(-1)}>
<Button color="inherit" onClick={() => handlePageChange(-1)} data-testid="backToSignInOptions">
Back
</Button>
{!passwordExists && (
<Button color="info" onClick={() => handlePageChange('/confirm-mnemonic')}>
<Button color="info" onClick={() => handlePageChange('/confirm-mnemonic')} data-testid="goToCreatePassword">
Create a password
</Button>
)}
@@ -29,6 +29,7 @@ export const SignInPassword = () => {
autoFocus
/>
<Button
data-testid="signInPasswordButton"
variant="contained"
size="large"
fullWidth
@@ -38,6 +39,7 @@ export const SignInPassword = () => {
</Button>
<Box display="flex" justifyContent="space-between">
<Button
data-testid="backToSignInOptionsFromPassword"
color="inherit"
disableElevation
onClick={() => {
@@ -49,6 +51,7 @@ export const SignInPassword = () => {
</Button>
<Button
data-testid="forgotPasswordButton"
color="info"
onClick={() => {
setError(undefined);
@@ -52,6 +52,7 @@ export const VerifyMnemonic = () => {
<Stack spacing={3} sx={{ width: 300 }}>
<Button
variant="contained"
data-testid="nextToStep3"
fullWidth
size="large"
disabled={currentSelection !== numberOfRandomWords}
@@ -59,7 +60,7 @@ export const VerifyMnemonic = () => {
>
Next
</Button>
<Button color="inherit" fullWidth size="large" onClick={() => navigate(-1)}>
<Button color="inherit" fullWidth size="large" onClick={() => navigate(-1)} data-testid="backToStep1">
Back
</Button>
</Stack>
@@ -19,6 +19,7 @@ export const WelcomeContent: React.FC<{}> = () => {
variant="contained"
size="large"
onClick={() => navigate('/existing-account')}
data-testid="signIn"
>
Sign in
</Button>
@@ -29,6 +30,7 @@ export const WelcomeContent: React.FC<{}> = () => {
disableElevation
size="large"
onClick={() => navigate('/create-mnemonic')}
data-testid="createAccount"
>
Create account
</Button>
+2 -3
View File
@@ -14,9 +14,8 @@ export const BalanceCard = () => {
return (
<NymCard
title="Balance"
data-testid="check-balance"
borderless
Action={<ClientAddress withCopy showEntireAddress />}
Action={<ClientAddress withCopy showEntireAddress/>}
>
<Grid container direction="column" spacing={2}>
<Grid item>
@@ -27,7 +26,7 @@ export const BalanceCard = () => {
)}
{!userBalance.error && (
<Typography
data-testid="refresh-success"
data-testid="nym-balance"
sx={{
color: 'text.primary',
textTransform: 'uppercase',
+1 -1
View File
@@ -30,7 +30,7 @@ export const Bond = () => {
<NymCard title="Bond" subheader="Bond a mixnode or gateway" noPadding>
{status === EnumRequestStatus.initial && (
<Box sx={{ px: 3, mb: 1 }}>
<Alert severity="warning">Always ensure you leave yourself enough funds to UNBOND</Alert>
<Alert severity="warning" data-testid="fundsAlert">Always ensure you leave yourself enough funds to UNBOND</Alert>
</Box>
)}
{ownership?.hasOwnership && (
-11
View File
@@ -5,7 +5,6 @@ import { DelegationWithEverything, FeeDetails, DecCoin } from '@nymproject/types
import { Link } from '@nymproject/react/link/Link';
import { AppContext, urls } from 'src/context/main';
import { DelegationList } from 'src/components/Delegation/DelegationList';
import { PendingEvents } from 'src/components/Delegation/PendingEvents';
import { TPoolOption } from 'src/components';
import { Console } from 'src/utils/console';
import { CompoundModal } from 'src/components/Rewards/CompoundModal';
@@ -49,7 +48,6 @@ export const Delegation: FC<{ isStorybook?: boolean }> = ({ isStorybook }) => {
const {
delegations,
pendingDelegations,
totalDelegations,
totalRewards,
isLoading,
@@ -322,15 +320,6 @@ export const Delegation: FC<{ isStorybook?: boolean }> = ({ isStorybook }) => {
</Stack>
</Paper>
{pendingDelegations && (
<Paper elevation={0} sx={{ p: 3, mt: 2 }}>
<Stack spacing={5}>
<Typography variant="h6">Pending Delegation Events</Typography>
<PendingEvents pendingEvents={pendingDelegations} explorerUrl={urls(network).networkExplorer} />
</Stack>
</Paper>
)}
{showNewDelegationModal && (
<DelegateModal
open={showNewDelegationModal}
+59
View File
@@ -0,0 +1,59 @@
import React, { useContext } from 'react';
import { Card, Divider, Grid, Typography } from '@mui/material';
import { useFormContext } from 'react-hook-form';
import { AppContext } from '../../context/main';
const SendReviewField = ({ title, subtitle, info }: { title: string; subtitle?: string; info?: boolean }) => (
<>
<Typography sx={{ color: info ? 'nym.fee' : '' }} data-testid={title}>
{title}
</Typography>
<Typography sx={{ color: info ? 'nym.fee' : '', wordBreak: 'break-all' }} data-testid={subtitle}>
{subtitle}
</Typography>
</>
);
export const SendReview = ({ transferFee }: { transferFee?: string }) => {
const { getValues } = useFormContext();
const { clientDetails } = useContext(AppContext);
const values = getValues();
return (
<Card
variant="outlined"
sx={{
width: '100%',
py: 3,
px: 2,
my: 3,
mx: 0,
}}
>
<Grid container spacing={2}>
<Grid item xs={12}>
<SendReviewField title="From" subtitle={clientDetails?.client_address} />
</Grid>
<Grid item xs={12}>
<Divider light />
</Grid>
<Grid item xs={12}>
<SendReviewField title="To" subtitle={values.to} />
</Grid>
<Grid item xs={12}>
<Divider light />
</Grid>
<Grid item xs={12}>
<SendReviewField title="Amount" subtitle={`${values.amount.amount} ${clientDetails?.denom}`} />
</Grid>
<Grid item xs={12}>
<Divider light />
</Grid>
<Grid item xs={12}>
<SendReviewField title="Transfer fee" subtitle={`${transferFee} ${clientDetails?.denom}`} info />
</Grid>
</Grid>
</Card>
);
};
+1 -1
View File
@@ -99,7 +99,7 @@ const TerminalInner: React.FC = () => {
<Box width="100%" display="flex" justifyContent="space-between">
<Box display="flex" alignItems="center">
<TerminalIcon sx={{ mr: 1 }} />
<Typography mr={4}>Terminal</Typography>
<Typography mr={4} data-testid='terminal-header'>Terminal</Typography>
{!isBusy && <RefreshIcon onClick={refresh} cursor="pointer" />}
</Box>
<CloseIcon onClick={handleShowTerminal} cursor="pointer" />
+1 -1
View File
@@ -19,5 +19,5 @@
"@assets/*": ["../assets/*"]
}
},
"exclude": ["node_modules", "dist", "jest.config.js", "webpack.config.js", "webpack.prod.js", "webpack.common.js", "target"]
"exclude": ["node_modules", "dist", "jest.config.js", "webpack.config.js", "webpack.prod.js", "webpack.common.js", "target", "wallet-ui-tests"]
}
@@ -0,0 +1,86 @@
import Balance from '../tests/pageobjects/balanceScreen'
import Auth from '../tests/pageobjects/authScreens'
const userData = require("../common/user-data.json");
const deleteScript = require("../scripts/deletesavedwallet")
const savedWalletScript = require("../scripts/savedwalletexists")
class Helpers {
// clear wallet data, login, and navigate to QA network
freshMnemonicLoginQaNetwork = async () => {
await deleteScript
await savedWalletScript
await Auth.loginWithMnemonic(userData.mnemonic)
await Balance.selectQa()
}
loginMnemonic = async () => {
await Auth.loginWithMnemonic(userData.mnemonic)
}
//helper to decode mnemonic so plain 24 character passphrase isn't in sight albeit it is presented when ruunning the scripts
// TO-DO figure out what's going on with the decoding bit
decodeBase = async (input) => {
var m = Buffer.from(input, "base64").toString();
return m;
}
navigateAndClick = async (element) => {
await element.waitForClickable({ timeout: 6000 })
await element.click();
}
elementVisible = async (element) => {
await element.waitForDisplayed({ timeout: 6000 })
}
elementClickable = async (element) => {
await element.toBeClickable({ timeout: 8000 })
}
addValueToTextField = async (element, value) => {
await element.addValue(value)
}
verifyStrictText = async (element, expectedText) => {
let error = await element.getText()
expect(error).toStrictEqual(expectedText)
}
verifyPartialText = async (element, expectedText) => {
let error = await element.getText()
expect(error).toContain(expectedText)
}
currentBalance = async (value) => {
return parseFloat(value.split(/\s+/)[0].toString()).toFixed(5)
}
calculateFees = async (beforeBalance, transactionFee, amount, isSend) => {
let fee
if (isSend) {
//send transaction
fee = transactionFee.split(/\s+/)[0]
} else {
//delegate transaction
fee = transactionFee.split(/\s+/)[3]
}
const currentBalance = beforeBalance.split(/\s+/)[0]
console.log("currenttttt 2 ............. = " + currentBalance)
const castCurrentBalance = parseFloat(currentBalance).toFixed(5)
console.log("castttt ............. " + castCurrentBalance)
const transCost = +parseFloat(amount) + +parseFloat(fee).toFixed(5)
console.log("trans ............." + transCost)
let sum = +castCurrentBalance - transCost
return sum.toFixed(5)
}
}
module.exports = new Helpers();
@@ -0,0 +1,42 @@
module.exports = {
//welcome, sign in, create account
homePageErrorMnemonic: "Error parsing bip39 mnemonic",
signInWithoutMnemonic: "A mnemonic must be provided",
signInRandomString: "mnemonic has a word count that is not a multiple of 6:",
signInIncorrectMnemonic: "mnemonic contains an unknown word",
incorrectMnemonicPasswordCreation: "The mnemonic provided is not valid. Please check the mnemonic",
invalidPasswordOnSignIn: "failed to decrypt the given data with the provided password",
signInWithoutPassword: "A password must be provided",
failedToFindWalletFile: "The wallet file is not found",
//headers
mnemonicSignIn: "Enter a mnemonic to sign in",
passwordSignIn: "Enter a password to sign in",
//homePage
qaNetwork: "QA",
sandboxNetwork: "Testnet Sandbox",
mainnetNetwork: "Nym Mainnet",
noNym: "0 NYM",
//send
invalidRecipientAddress: "123",
recipientAddress: "n17tj0a0w6v7r2dc54rnkzfza6s8hxs87rj273a5",
amountToSend: "1",
negativeAmount: "-1",
inferiorAmount: "0.0000001",
confirmedAmount: "1 NYM",
sendDetails: "Send details",
// bond
host: "1.1.1.1",
version: "1.2.1",
// user incorrect data
incorrectMnemonic: "giraffe note order sun cradle bottom crime humble able antique rural donkey guess parent potato tongue truly way disagree exile zebra someone else heat",
randomString:"thisrandomstring",
password:"iAmThePassword1!",
incorrectPassword:"123notvalid",
};
@@ -0,0 +1,9 @@
{
"mnemonic": "giraffe note order sun cradle bottom crime humble able antique rural donkey guess parent potato tongue truly way disagree exile zebra someone else typical",
"qa_address": "n1qqct7gs79yrjncpkumljxeqjsnwvn42j2g3fw4",
"receiver_address": "n167rupnmpput2alw62sz43eelks03zek4fwvjk0",
"amount_to_send": "1",
"identity_key_to_delegate_mix_node": "HqW2HStFHtAZ3PxRaiSCh7xJK6B7swoR1gSmJzH2iV9g",
"identity_key_to_delegate_gateway": "",
"delegate_amount": "10"
}
+31
View File
@@ -0,0 +1,31 @@
{
"name": "wallet-ui-tests",
"version": "1.0.0",
"description": "ui tests for the nym wallet",
"scripts": {
"test": "wdio run wdio.conf.ts",
"test:signup": "wdio run wdio.conf.ts --suite signup",
"test:login": "wdio run wdio.conf.ts --suite login",
"test:balance": "wdio run wdio.conf.ts --suite balance",
"test:nav": "wdio run wdio.conf.ts --suite nav",
"test:send": "wdio run wdio.conf.ts --suite send",
"test:delegation": "wdio run wdio.conf.ts --suite delegation"
},
"author": "",
"license": "MIT",
"dependencies": {
"-": "^0.0.1",
"@wdio/cli": "^7.16.16",
"save-dev": "^0.0.1-security",
"@zxing/browser": "^0.0.9",
"ts-node": "^10.6.0"
},
"devDependencies": {
"@wdio/local-runner": "^7.16.16",
"@wdio/mocha-framework": "^7.16.15",
"@wdio/spec-reporter": "^7.16.14",
"prettier": "2.5.1",
"typescript": "^4.6.2"
}
}
@@ -0,0 +1,9 @@
const { exec } = require("child_process")
const deleteSavedFile = exec("rm '/home/benedetta/.local/share/nym-wallet/saved-wallet.json'", (err, stdout, stderr) => {
if (err) {
console.error(`${err.message}`)
return
} else
console.log("File deleted")
})
@@ -0,0 +1,14 @@
const { exec } = require("child_process")
// const doesFileExist = exec("test -f /home/benedetta/.local/share/nym-wallet/saved-wallet.json" && "echo '$FILE exists.'" || "echo 'file doesn't exist'")
// scriptExist ? expect(getErrorWarning).toStrictEqual(textConstants.invalidPasswordOnSignIn) : expect(getErrorWarning).toStrictEqual(textConstants.failedToFindWalletFile)
const doesFileExist = exec("test -f /home/benedetta/.local/share/nym-wallet/saved-wallet.json", (err, stdout, stderr) => {
if (err) {
console.error(`${err.message}`)
return
} else
console.log("File: " + stdout)
})
@@ -0,0 +1,21 @@
class Nav {
get lightMode() { return $("[data-testid='LightModeOutlinedIcon']") }
get darkMode() { return $("[data-testid='ModeNightOutlinedIcon']") }
get terminalTitle() { return $("[data-testid='terminal-header']") }
get terminalIcon() { return $("[data-testid='TerminalIcon']") }
get balance() { return $("[data-testid='Balance']") }
get send() { return $("[data-testid='Send']") }
get receive() { return $("[data-testid='Receive']") }
get bond() { return $("[data-testid='Bond']") }
get unbond() { return $("[data-testid='Unbond']") }
get delegation() { return $("[data-testid='Delegation']") }
get closeIcon() { return $("[data-testid='CloseIcon']") }
}
export default new Nav()
@@ -0,0 +1,74 @@
import Balance from '../pageobjects/balanceScreen'
class Auth {
//Welcome landing page
get signInButton() { return $("[data-testid='signIn']") }
get createAccount() { return $("[data-testid='createAccount']") }
// Existing account sign in option page
get signInMnemonic() { return $("[data-testid='signInWithMnemonic']") }
get signInPassword() { return $("[data-testid='signInWithPassword']") }
get backToWelcomePage() { return $("[data-testid='backToWelcomePage']") }
get forgotPassword() { return $("[data-testid='forgotPassword']") }
// Sign in with mnemonic page
get mnemonicLoginScreenHeader() { return $("[data-testid='Enter a mnemonic to sign in']") }
get mnemonicInput() { return $("[data-testid='mnemonicInput']") }
get signIn() { return $("[data-testid='signInSubmitButton']") }
get backToSignInOptions() { return $("[data-testid='backToSignInOptions']") }
get createPassword() { return $("[data-testid='goToCreatePassword']") }
// Create password step 1/2
get backToMnemonicSignIn() { return $("[data-testid='backToMnemonicSignIn']") }
get nextToPasswordCreation() { return $("[data-testid='nextToPasswordCreation']") }
// Create password step 2/2
get password() { return $("[data-testid='Password']") }
get confirmPassword() { return $("[data-testid='Confirm password']") }
get createPasswordButton() { return $("[data-testid='createPasswordButton']") }
get backToStep1PasswordCreation() { return $("[data-testid='backToStep1PasswordCreation']") }
// Create account step 1/3
get copyMnemonic() { return $("[data-testid='copyMnemonic']") }
get iSavedMnemonic() { return $("[data-testid='iSavedMnemonic']") }
get mnemonicPhrase() { return $("[data-testid='mnemonicPhrase']") }
get backToWelcomePageFromCreate() { return $("[data-testid='backToWelcome']") }
// Create account step 2/3
get wordIndex() { return $$("[data-testid='wordIndex']") }
get mnemonicWordTile() { return $$("[data-testid='mnemonicWordTile']") }
get nextToStep3() { return $("[data-testid='nextToStep3']") }
get backToStep1() { return $("[data-testid='backToStep1']") }
// Create account step 3/3
get nextStorePassword() { return $("[data-testid='nextStorePassword']") }
get skipPasswordAndSignInWithMnemonic() { return $("[data-testid='skipPasswordAndSignInWithMnemonic']") }
// Enter password to sign in
get passwordLoginScreenHeader() { return $("[data-testid='Enter a password to sign in']") }
get enterPassword() { return $("[data-testid='Enter password']") }
get signInPasswordButton() { return $("[data-testid='signInPasswordButton']") }
get backToSignInOptionsFromPassword() { return $("[data-testid='backToSignInOptionsFromPassword']") }
get forgotPasswordButton() { return $("[data-testid='forgotPasswordButton']") }
// Errors
get error() { return $("[data-testid='error']") }
//TO-DO get this bit below working
getErrorMessage = async () => {
await (await this.error).waitForDisplayed({ timeout: 1500 })
await this.error.getText()
}
//login to the application
loginWithMnemonic = async (mnemonic) => {
await this.signInButton.click()
await this.signInMnemonic.click()
await this.mnemonicInput.waitForDisplayed()
await this.mnemonicInput.addValue(mnemonic);
await this.signIn.click();
await Balance.nymBalance.isExisting({ timeout: 4000 });
};
}
export default new Auth()
@@ -0,0 +1,23 @@
class Balance {
get balance() { return $("[data-testid='Balance']") }
get checkBalance() { return $("[data-testid='check-balance']") }
get nymBalance() { return $("[data-testid='nym-balance']") }
get copyAccountId() { return $("[data-testid='copyIcon']") }
get accountNumber() { return $("[data-testid='accountNumber']") }
get networkDropdown() { return $("[data-testid='ArrowDropDownIcon']") }
get networkEnv() { return $("[data-testid='networkEnv']") }
get networkSelectQa() { return $("[data-testid='QA']") }
selectQa = async () => {
await this.networkDropdown.waitForDisplayed({ timeout: 4000 })
await this.networkDropdown.click()
await this.networkSelectQa.waitForClickable({ timeout: 4000 })
await this.networkSelectQa.click()
await this.networkEnv.waitForClickable({ timeout: 2000 })
}
}
export default new Balance()
@@ -0,0 +1,10 @@
class Bond {
get bondTitle() { return $("[data-testid='Bond']") }
get mixnodeRadio() { return $("[data-testid='mix-node']") }
get gatewayRadio() { return $("[data-testid='gate-way']") }
get fundsAlert() { return $("[data-testid='fundsAlert']") }
}
export default new Bond()
@@ -0,0 +1,9 @@
class Delegation {
get delegationTitle() { return $("[data-testid='Delegation']") }
get delegateStakeButton() { return $("[data-testid='Delegate stake']") }
get delegateModalHeader() { return $("[data-testid='Delegate']") }
}
export default new Delegation()
@@ -0,0 +1,7 @@
class Receive {
get receiveNymTitle() { return $("[data-testid='Receive NYM']") }
}
export default new Receive()
@@ -0,0 +1,28 @@
class Send {
// send nym form
get sendHeader() { return $("[data-testid='Send']") }
get recipientAddress() { return $("[data-testid='recipientAddress']") }
// get sendAmount() { return $("[data-testid='Amount']") }
get sendAmount() { return $("#mui-5") } // TO-DO fix this selector, using #mui-5 isn't a good solution
get next() { return $("[data-testid='Next']") }
// confirm transaction modal
get sendDetailsHeader() { return $("[data-testid='Send details']") }
get from() { return $("/html/body/div[2]/div[3]/div[2]/div[1]/div[1]") }
get to() { return $("/html/body/div[2]/div[3]/div[2]/div[2]") }
get amount() { return $("/html/body/div[2]/div[3]/div[2]/div[3]") }
get fee() { return $("/html/body/div[2]/div[3]/div[2]/div[4]") }
get confirm() { return $("[data-testid='Confirm']") }
// transaction sent
get viewOnBlockchain() { return $("[data-testid='viewOnBlockchain']") }
get done() { return $("[data-testid='Done']") }
}
export default new Send()
@@ -0,0 +1,7 @@
class Unbond {
get unbondTitle() { return $("[data-testid='Unbond']") }
}
export default new Unbond()
@@ -0,0 +1,32 @@
import Balance from '../../pageobjects/balanceScreen'
import Auth from '../../pageobjects/authScreens'
const textConstants = require("../../../common/text-constants");
const userData = require("../../../common/user-data.json");
const Helper = require('../../../common/helper');
describe('Balance screen displays correctly', () => {
it('selecting qa network', async () => {
//log in
await Helper.loginMnemonic()
// select QA network
await Helper.navigateAndClick(Balance.networkDropdown)
await Helper.navigateAndClick(Balance.networkSelectQa)
// verifty QA network has been selected properly
await Helper.verifyStrictText(Balance.networkEnv, textConstants.qaNetwork)
})
it('copy the account id', async () => {
// ensure the account number contains *something*
await Helper.elementVisible(Balance.accountNumber)
await Helper.verifyPartialText(Balance.accountNumber[1],'1')
await Helper.navigateAndClick(Balance.copyAccountId)
// TO-DO is there a way to verify that the copy worked, aka pasting it somewhere maybe?
})
})
@@ -0,0 +1,21 @@
import Balance from '../../pageobjects/balanceScreen'
import Auth from '../../pageobjects/authScreens'
import Nav from '../../pageobjects/appNavConstants'
import Delegation from '../../pageobjects/delegationScreen'
import Send from '../../pageobjects/sendScreen'
const Helper = require('../../../common/helper');
const textConstants = require("../../../common/text-constants");
const userData = require("../../../common/user-data.json");
describe('Delegate to a mixnode', () => {
it('entering an invalid node identity key', async () => {
//login and navigate to the screen
await Helper.freshMnemonicLoginQaNetwork()
await Helper.navigateAndClick(Nav.delegation)
await Helper.elementVisible(Delegation.delegationTitle)
// TO-DO enter an invalid node
})
})
@@ -0,0 +1,99 @@
import Auth from '../../pageobjects/authScreens'
import Balance from '../../pageobjects/balanceScreen'
import ValidatorClient from '@nymproject/nym-validator-client';
const deleteScript = require("../../../scripts/deletesavedwallet")
const textConstants = require("../../../common/text-constants");
const userData = require("../../../common/user-data.json");
const Helper = require('../../../common/helper');
describe('Create password for existing account and use it to sign in', () => {
it('enter incorrect mnemonic', async () => {
//click through sign in
await Helper.navigateAndClick(Auth.signInButton)
await Helper.navigateAndClick(Auth.signInMnemonic)
//instead of entering mnemonic, click on create a password
await Helper.navigateAndClick(Auth.createPassword)
//enter incorrect mnemonic
await Helper.addValueToTextField(Auth.mnemonicInput, textConstants.incorrectMnemonic)
await Helper.navigateAndClick(Auth.nextToPasswordCreation)
// assert error message is correct
await Helper.verifyStrictText(Auth.error, textConstants.incorrectMnemonicPasswordCreation)
})
it('enter random string', async () => {
// enter random string as mnemonic
await Helper.addValueToTextField(Auth.mnemonicInput, textConstants.randomString)
await Helper.navigateAndClick(Auth.nextToPasswordCreation)
// assert error is correct
await Helper.verifyStrictText(Auth.error, textConstants.incorrectMnemonicPasswordCreation)
})
it('enter correct mnemonic', async () => {
// generate random mnemonic in the backend
const randomMnemonic = ValidatorClient.randomMnemonic();
deleteScript
// use it to continue with password creation flow
await Helper.navigateAndClick(Auth.backToMnemonicSignIn)
await Helper.navigateAndClick(Auth.createPassword)
await Helper.addValueToTextField(Auth.mnemonicInput, randomMnemonic)
await Helper.navigateAndClick(Auth.nextToPasswordCreation)
await Helper.elementVisible(Auth.password)
})
it('create an invalid password', async () => {
// type an invalid password in both fields
await Helper.addValueToTextField(Auth.password, textConstants.incorrectPassword)
await Helper.navigateAndClick(Auth.confirmPassword)
await Helper.addValueToTextField(Auth.confirmPassword, textConstants.incorrectPassword)
// ensure the button to proceed is still disabled
const nextButton = await Auth.createPasswordButton
const isNextDisabled = await nextButton.getAttribute('disabled')
expect(isNextDisabled).toBe("true")
})
it('create a valid password', async () => {
// type a valid password in both fields
await Helper.navigateAndClick(Auth.password)
await Helper.addValueToTextField(Auth.password, textConstants.password)
await Helper.navigateAndClick(Auth.confirmPassword)
await Helper.addValueToTextField(Auth.confirmPassword, textConstants.password)
// verify the password is created and the next screen is visible
await Helper.navigateAndClick(Auth.createPasswordButton)
await Helper.verifyStrictText(Auth.passwordLoginScreenHeader, textConstants.passwordSignIn)
})
it('sign in with no password throws error', async () => {
//click sign without entering a password
await Helper.navigateAndClick(Auth.signInPasswordButton)
// wait for error
await Helper.elementVisible(Auth.error)
// verify error has the correct message
await Helper.verifyStrictText(Auth.error, textConstants.signInWithoutPassword)
})
it('sign in with invalid password throws error', async () => {
// enter invalid password
await Helper.addValueToTextField(Auth.enterPassword, textConstants.incorrectPassword)
await Helper.navigateAndClick(Auth.signInPasswordButton)
// wait for error
await Helper.elementVisible(Auth.error)
await Helper.verifyStrictText(Auth.error, textConstants.invalidPasswordOnSignIn)
})
})
@@ -0,0 +1,67 @@
import Auth from '../../pageobjects/authScreens'
import Balance from '../../pageobjects/balanceScreen'
import ValidatorClient from '@nymproject/nym-validator-client';
import { text } from 'stream/consumers';
const textConstants = require("../../../common/text-constants");
const userData = require("../../../common/user-data.json");
const Helper = require('../../../common/helper');
describe('Wallet sign in functionality with mnemonic', () => {
it('get to the sign in with mnemonic screen', async () => {
// click through to reach the mnemonic sign in
await Helper.navigateAndClick(Auth.signInButton)
await Helper.navigateAndClick(Auth.signInMnemonic)
// verify you are on the right screen by confirming the header
await Helper.verifyStrictText(Auth.mnemonicLoginScreenHeader, textConstants.mnemonicSignIn)
})
it('sign in with no mnemonic throws error', async () => {
await Helper.navigateAndClick(Auth.signIn)
// wait for error
await Helper.elementVisible(Auth.error)
// verify error has the correct message
await Helper.verifyStrictText(Auth.error, textConstants.signInWithoutMnemonic)
})
it('sign in with incorrect mnemonic throws error', async () => {
// enter an incorrect mnemonic string
await Helper.addValueToTextField(Auth.mnemonicInput, textConstants.incorrectMnemonic)
await Helper.navigateAndClick(Auth.signIn)
// verifty error message is correct
await Helper.verifyPartialText(Auth.error, textConstants.signInIncorrectMnemonic)
})
it('sign in with random string throws error', async () => {
// enter a random string not in mnemonic "format"
await Helper.addValueToTextField(Auth.mnemonicInput, textConstants.randomString)
await Helper.navigateAndClick(Auth.signIn)
// verifty error message is correct
await Helper.verifyPartialText(Auth.error, textConstants.signInRandomString)
})
it('should sign in with valid credentials', async () => {
// create new mnemonic
const randomMnemonic = ValidatorClient.randomMnemonic();
// enter mnemonic
await Helper.addValueToTextField(Auth.mnemonicInput, randomMnemonic)
await Helper.navigateAndClick(Auth.signIn)
// verify successful login, balance is visible
await Helper.elementVisible(Balance.balance)
//new accounts will always default to mainnet, so 0 balance
// TO-DO this value sometimes returns " " instead of "0"
await Helper.verifyStrictText(Balance.nymBalance, textConstants.noNym)
})
})
@@ -0,0 +1,29 @@
import Auth from '../../pageobjects/authScreens'
import Balance from '../../pageobjects/balanceScreen'
const textConstants = require("../../../common/text-constants");
const userData = require("../../../common/user-data.json");
const deleteWallet = require("../../../scripts/deletesavedwallet");
const walletExists = require("../../../scripts/savedwalletexists")
const Helper = require('../../../common/helper');
describe('Wallet sign in functionality without creating password', () => {
it('sign in with invalid password and no saved wallet.json file throws error', async () => {
// delete existing saved wallet file
deleteWallet
//click through sign without entering a password
await Helper.navigateAndClick(Auth.signInButton)
await Helper.navigateAndClick(Auth.signInPassword)
// enter invalid password
await Helper.addValueToTextField(Auth.enterPassword,textConstants.incorrectPassword)
await Helper.navigateAndClick(Auth.signInPasswordButton)
// wait for error
await Helper.elementVisible(Auth.error)
// verify error has the correct message
await Helper.verifyStrictText(Auth.error, textConstants.failedToFindWalletFile)
})
})
@@ -0,0 +1,73 @@
import Auth from '../../pageobjects/authScreens'
import Nav from '../../pageobjects/appNavConstants'
import Balance from '../../pageobjects/balanceScreen'
import Send from '../../pageobjects/sendScreen'
import Receive from '../../pageobjects/receiveScreen'
import Bond from '../../pageobjects/bondScreen'
import Unbond from '../../pageobjects/unbondScreen'
import Delegation from '../../pageobjects/delegationScreen'
const userData = require("../../../common/user-data.json");
const Helper = require('../../../common/helper');
describe('Nav Items behave correctly', () => {
it('switch from light to dark mode and back', async () => {
//log in
await Helper.freshMnemonicLoginQaNetwork()
// click on different modes
await Helper.navigateAndClick(Nav.lightMode)
await Helper.navigateAndClick(Nav.darkMode)
await Helper.elementVisible(Nav.lightMode)
})
it('clicking terminal opens the modal', async () => {
// ensure the terminal button opens the terminal
await Helper.elementVisible(Nav.terminalIcon)
await Helper.navigateAndClick(Nav.terminalIcon)
await Helper.elementVisible(Nav.terminalTitle)
await Helper.verifyPartialText(Nav.terminalTitle, 'Terminal')
})
})
describe('Menu items lead to correct screen', () => {
//TO-DO none of this works
//check each menu item opens the right screen/modal
it('check Balance link works', async () => {
await Helper.navigateAndClick(Nav.balance)
await Helper.verifyPartialText(Balance.balance, 'Balance')
})
it('check Send link works', async () => {
await Helper.navigateAndClick(Nav.send)
await Helper.verifyPartialText(Send.sendHeader, 'Send')
await Helper.navigateAndClick(Nav.closeIcon)
})
it('check Receive link works', async () => {
await Helper.navigateAndClick(Nav.receive)
await Helper.verifyPartialText(Receive.receiveNymTitle, 'Receive NYM')
})
it('check Bond link works', async () => {
await Helper.navigateAndClick(Nav.bond)
await Helper.verifyPartialText(Bond.bondTitle, 'Bond')
})
it('check Unbond link works', async () => {
await Helper.navigateAndClick(Nav.unbond)
await Helper.verifyPartialText(Unbond.unbondTitle, 'Unbond')
})
it('check Delegation link works', async () => {
await Helper.navigateAndClick(Nav.delegation)
await Helper.verifyPartialText(Delegation.delegationTitle, 'Delegation')
})
})
@@ -0,0 +1,101 @@
import Auth from '../../pageobjects/authScreens'
import Balance from '../../pageobjects/balanceScreen'
const textConstants = require("../../../common/text-constants");
const userData = require("../../../common/user-data.json");
const deleteScript = require("../../../scripts/deletesavedwallet")
const Helper = require('../../../common/helper');
describe('Create a new account and verify it exists', () => {
it('generate new mnemonic and verify mnemonic words', async () => {
// delete any existing saved-wallet.json
deleteScript
// click through create account flow
await Helper.navigateAndClick(Auth.createAccount)
await Helper.elementVisible(Auth.mnemonicPhrase)
// save mnemonic phrase
let mnemonic = await (await Auth.mnemonicPhrase).getText()
let arrayMnemonic = mnemonic.split(" ")
await Helper.navigateAndClick(Auth.copyMnemonic)
await Helper.navigateAndClick(Auth.iSavedMnemonic)
// verify the mnemonic words in the correct order
let mnemonicWordTiles = await (await Auth.mnemonicWordTile)
let wordTileIndex = await (await Auth.wordIndex)
const wordsArray: any[] = []
for (const word of mnemonicWordTiles) {
const wordText = await word.getText()
const index = arrayMnemonic.indexOf(wordText)
wordsArray.push({ word, index })
}
for (const index of wordTileIndex) {
const indexValue = await index.getText()
const match = wordsArray.find((word) => +word.index === +indexValue - 1)
if (match) {
await match.word.click()
}
}
// ensure that once the task above is complete, the 'next' button is enabled
const nextButton = await Auth.nextToStep3
const isNextDisabled = await nextButton.getAttribute('disabled')
expect(isNextDisabled).toBe(null)
})
it('click skip password', async () => {
// click on skip password creation
await Helper.navigateAndClick(Auth.nextToStep3)
await Helper.navigateAndClick(Auth.skipPasswordAndSignInWithMnemonic)
// can see mnemonic login page
await Helper.elementVisible(Auth.mnemonicInput)
await Helper.navigateAndClick(Auth.backToSignInOptions)
})
it('set up invalid password for new account', async () => {
// enter invalid password in both fields
await Helper.navigateAndClick(Auth.password)
await Helper.addValueToTextField(Auth.password, textConstants.incorrectPassword)
await Helper.navigateAndClick(Auth.confirmPassword)
await Helper.addValueToTextField(Auth.confirmPassword, textConstants.incorrectPassword)
// verify that the 'next' button is still disabled
const nextButton = await Auth.nextStorePassword
const isNextDisabled = await nextButton.getAttribute('disabled')
expect(isNextDisabled).toBe("true")
})
it('set up valid password for new account', async () => {
// enter a valid password in both fields
await Helper.navigateAndClick(Auth.password)
await Helper.addValueToTextField(Auth.password, textConstants.password)
await Helper.navigateAndClick(Auth.confirmPassword)
await Helper.addValueToTextField(Auth.confirmPassword, textConstants.password)
// verify that the 'next' button is clickable
const nextButton = await Auth.nextStorePassword
const isNextDisabled = await nextButton.getAttribute('disabled')
expect(isNextDisabled).toBe(null)
})
it('proceed to login with newly created password', async () => {
// login with a password
await Helper.navigateAndClick(Auth.nextStorePassword)
await Helper.navigateAndClick(Auth.enterPassword)
await Helper.addValueToTextField(Auth.enterPassword, textConstants.password)
await Helper.navigateAndClick(Auth.signInPasswordButton)
// TO-DO for some reason this is failing due to failed to decrypt the wallet etc error
await Helper.elementVisible(Balance.balance)
//new accounts will always default to mainnet, so 0 balance
await Helper.verifyStrictText(Balance.nymBalance, textConstants.noNym)
})
})
@@ -0,0 +1,62 @@
import Balance from '../../pageobjects/balanceScreen'
import Auth from '../../pageobjects/authScreens'
import Nav from '../../pageobjects/appNavConstants'
import Send from '../../pageobjects/sendScreen'
const Helper = require('../../../common/helper');
const textConstants = require("../../../common/text-constants");
const userData = require("../../../common/user-data.json");
describe.skip('Send modal functions correctly', () => {
it('entering an invalid recipient address shows error', async () => {
// sign in with mnemonic and select QA
await Helper.freshMnemonicLoginQaNetwork()
// click on send and check modal appears
await Helper.navigateAndClick(Nav.send)
await Helper.elementVisible(Send.sendHeader)
// add an invalid recipient address
await Helper.addValueToTextField(Send.recipientAddress, textConstants.invalidRecipientAddress)
// TO-DO -- question: should there not be an error message before clicking on Next to warn that the address is invalid?
})
it('entering an valid recipient address with negative amount value shows error', async () => {
await Helper.navigateAndClick(Send.recipientAddress)
// TO-DO figure out how to clear a text field before adding new value
await (Send.recipientAddress).clearValue()
await Helper.addValueToTextField(Send.recipientAddress, userData.receiver_address)
await Helper.navigateAndClick(Send.sendAmount)
await Helper.addValueToTextField(Send.sendAmount, textConstants.negativeAmount)
//next button is still disabled and error message appears
const nextButton = await Send.next
const isNextDisabled = await nextButton.getAttribute('disabled')
expect(isNextDisabled).toBe("true")
})
it('enter a valid recipient and value', async () => {
// enter valid data
await Helper.addValueToTextField(Send.recipientAddress, userData.receiver_address)
const getCurrentBalance = await (await Balance.nymBalance).getText()
await Helper.addValueToTextField(Send.sendAmount, textConstants.amountToSend)
// click on next and verify details
await Helper.navigateAndClick(Send.next)
const fee = await (await Send.fee).getText()
await Helper.verifyPartialText(Send.sendDetailsHeader, textConstants.sendDetails)
await Helper.verifyPartialText(Send.amount, textConstants.confirmedAmount)
await Helper.navigateAndClick(Send.confirm)
await Helper.elementVisible(Send.viewOnBlockchain)
await Helper.elementClickable(Send.done)
// calculate the transaction and verify it has been correctly executed
let sumCost = await Helper.calculateFees(getCurrentBalance, fee, textConstants.amountToSend, true)
const getNewBalance = await Balance.nymBalance.getText()
await Helper.navigateAndClick(Send.done)
// TO-DO the following fails with "TypeError: elem[prop] is not a function"
expect(getNewBalance).toEqual(sumCost)
})
})

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