Compare commits

..

35 Commits

Author SHA1 Message Date
Gala efdb8adab8 improve typing 2022-08-09 15:05:22 +02:00
Gala e8632c3762 Merge branch 'develop' into 306-wallet-balance-ui 2022-08-09 14:59:51 +02:00
Jędrzej Stuczyński fa354016e0 Removed useless into() conversion 2022-08-08 09:49:52 +01:00
Tommy Verrall 935ee765e9 Merge pull request #1498 from nymtech/feature/fix-envs
fix .env files for explorer.
2022-08-05 16:36:08 +01:00
tommy 4c8e59e6fc fix .env files for explorer. 2022-08-05 17:11:35 +02:00
Bogdan-Ștefan Neacşu 067f3e6f1a validator-api: handle SIGTERM (#1496)
* validator-api: handle SIGTERM

* Update CHANGELOG
2022-08-05 15:59:34 +03:00
Gala fb0b928598 Merge branch '306-wallet-balance-ui' of github.com:nymtech/nym into 306-wallet-balance-ui 2022-08-05 13:30:44 +02:00
Gala e0e9d03b7f Merge branch 'develop' into 306-wallet-balance-ui 2022-08-05 13:29:32 +02:00
Gala 6f09d46dce Merge pull request #1492 from nymtech/feature/delegating_ui_update
feat(wallet-delegation): new confirmation modal ui
2022-08-05 12:08:51 +02:00
Gala bdef48331b Merge pull request #1489 from nymtech/332-fix-vesting-ui
Wallet: Fixing dark mode colours in vesting shedule
2022-08-05 12:05:19 +02:00
Gala 51a6936e51 Merge pull request #1491 from nymtech/330-mix-wallet-ui
Wallet: Fix light mode bg and nav bar UI
2022-08-05 12:02:50 +02:00
Gala fd456d2952 Merge pull request #1490 from nymtech/334-ne-changes
NE: Filter by use routing score instead of stake parameter and adding filters info
2022-08-04 18:24:04 +02:00
Gala eee1abe593 removing extra styles 2022-08-04 18:15:12 +02:00
Gala fffad43937 Avoiding CAPS in button 2022-08-04 17:51:24 +02:00
Gala 3a79f43a8d styling 2022-08-04 17:23:32 +02:00
pierre 2e495f87ab refactor(wallet-delegation): ModalListItem component, pass colon in label value 2022-08-04 16:35:14 +02:00
Gala 57a9f18f5a fixing padding marging and wording 2022-08-04 16:30:33 +02:00
Gala 0c6a0a9cae Merge branch 'develop' into 334-ne-changes 2022-08-04 15:18:54 +02:00
Gala c80d8d354a adding nodes number info 2022-08-04 15:17:44 +02:00
Gala 3f544dbc69 adding Advanced filtering text and hover fix 2022-08-04 14:59:17 +02:00
pierre d1e1f15db0 Merge branch 'develop' into feature/delegating_ui_update 2022-08-04 13:26:41 +02:00
pierre 651c314182 feat(wallet-delegation): new confirmation modal ui 2022-08-04 13:20:00 +02:00
Gala a57545521d some refactor 2022-08-04 11:28:04 +02:00
Gala da60606921 hover bg color in dark and light mode 2022-08-03 17:20:46 +02:00
Gala 14f9bf7234 left nav spacing 2022-08-03 16:50:02 +02:00
Gala c1fa92869a fix light bg color 2022-08-03 16:49:47 +02:00
Gala c8533e3ec8 cleaning 2022-08-03 14:15:03 +02:00
Gala 06c4dd601d filter by use routing score instead of stake parameter 2022-08-03 14:06:33 +02:00
Gala 4ff80bbab2 fixing dark mode progress bar 2022-08-03 14:02:50 +02:00
Gala d7220b1fec adding info text in filters and renaming 2022-08-03 13:42:48 +02:00
Gala d92df9ada3 fixing dark mode colours 2022-08-03 12:24:21 +02:00
Gala 78ace473c7 balance page ui 2022-07-19 12:12:01 +02:00
Gala e877dfe224 buton hover bg and text 2022-07-19 12:12:01 +02:00
Gala 30c488484c balance page ui 2022-07-19 11:55:35 +02:00
Gala 8cd155b2ac buton hover bg and text 2022-07-18 17:53:28 +02:00
77 changed files with 347 additions and 1808 deletions
+2
View File
@@ -37,6 +37,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
- validator: fixed local docker-compose setup to work on Apple M1 ([#1329])
- explorer-api: listen out for SIGTERM and SIGQUIT too, making it play nicely as a system service ([#1482]).
- network-requester: fix filter for suffix-only domains ([#1487])
- validator-api: listen out for SIGTERM and SIGQUIT too, making it play nicely as a system service ([#1496]).
### Changed
@@ -77,6 +78,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
[#1478]: https://github.com/nymtech/nym/pull/1478
[#1482]: https://github.com/nymtech/nym/pull/1482
[#1487]: https://github.com/nymtech/nym/pull/1487
[#1496]: https://github.com/nymtech/nym/pull/1496
## [nym-connect-v1.0.1](https://github.com/nymtech/nym/tree/nym-connect-v1.0.1) (2022-07-22)
Generated
+1 -32
View File
@@ -3325,6 +3325,7 @@ dependencies = [
"serde",
"serde_json",
"sqlx",
"task",
"thiserror",
"time 0.3.9",
"tokio",
@@ -5397,15 +5398,6 @@ dependencies = [
"tokio",
]
[[package]]
name = "streaming-stats"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0d670ce4e348a2081843569e0f79b21c99c91bb9028b3b3ecb0f050306de547"
dependencies = [
"num-traits",
]
[[package]]
name = "stringprep"
version = "0.1.2"
@@ -6293,29 +6285,6 @@ dependencies = [
"vesting-contract-common",
]
[[package]]
name = "validator-client-scripts"
version = "0.1.0"
dependencies = [
"base64",
"bip39",
"bs58",
"clap 3.2.8",
"csv",
"dotenv",
"log",
"mixnet-contract-common",
"network-defaults",
"pretty_env_logger",
"serde",
"serde_json",
"streaming-stats",
"tokio",
"url",
"validator-client",
"vesting-contract-common",
]
[[package]]
name = "valuable"
version = "0.1.0"
+1 -2
View File
@@ -68,8 +68,7 @@ members = [
"service-providers/network-statistics",
"validator-api",
"validator-api/validator-api-requests",
"tools/ts-rs-cli",
"tools/validator-client-scripts"
"tools/ts-rs-cli"
]
default-members = [
+1 -1
View File
@@ -507,7 +507,7 @@ mod test {
for (expected, raw_display) in values {
let coin = DecCoin {
denom: Network::MAINNET.mix_denom().display.into(),
denom: Network::MAINNET.mix_denom().display,
amount: raw_display.parse().unwrap(),
};
let base = reg.attempt_convert_to_base_coin(coin).unwrap();
+1
View File
@@ -1,5 +1,6 @@
EXPLORER_API_URL=https://explorer.nymtech.net/api/v1
VALIDATOR_API_URL=https://validator.nymtech.net
VALIDATOR_URL=https://rpc.nyx.nodes.guru
BIG_DIPPER_URL=https://blocks.nymtech.net
CURRENCY_DENOM=unym
CURRENCY_STAKING_DENOM=unyx
+2 -1
View File
@@ -1,5 +1,6 @@
EXPLORER_API_URL=https://qa-explorer.nymtech.net/api/v1
VALIDATOR_API_URL=https://qa-validator.nymtech.net
VALIDATOR_API_URL=https://qa-validator-api.nymtech.net
VALIDATOR_URL=https://qa-validator.nymtech.net
BIG_DIPPER_URL=https://qa-blocks.nymtech.net
CURRENCY_DENOM=unymt
CURRENCY_STAKING_DENOM=unyxt
+2 -1
View File
@@ -1,6 +1,7 @@
// master APIs
export const API_BASE_URL = process.env.EXPLORER_API_URL;
export const VALIDATOR_API_BASE_URL = process.env.VALIDATOR_API_URL;
export const VALIDATOR_URL = process.env.VALIDATOR_URL;
export const BIG_DIPPER = process.env.BIG_DIPPER_URL;
// specific API routes
@@ -9,7 +10,7 @@ export const MIXNODE_PING = `${API_BASE_URL}/ping`;
export const MIXNODES_API = `${API_BASE_URL}/mix-nodes`;
export const MIXNODE_API = `${API_BASE_URL}/mix-node`;
export const GATEWAYS_API = `${VALIDATOR_API_BASE_URL}/api/v1/gateways`;
export const VALIDATORS_API = `${VALIDATOR_API_BASE_URL}/validators`;
export const VALIDATORS_API = `${VALIDATOR_URL}/validators`;
export const BLOCK_API = `${VALIDATOR_API_BASE_URL}/block`;
export const COUNTRY_DATA_API = `${API_BASE_URL}/countries`;
export const UPTIME_STORY_API = `${VALIDATOR_API_BASE_URL}/api/v1/status/mixnode`; // add ID then '/history' to this.
+15 -8
View File
@@ -6,7 +6,6 @@ import {
DialogContent,
DialogActions,
DialogTitle,
IconButton,
Slider,
Typography,
Box,
@@ -25,6 +24,7 @@ import { useIsMobile } from '../../hooks/useIsMobile';
const FilterItem = ({
label,
id,
tooltipInfo,
value,
marks,
scale,
@@ -36,6 +36,7 @@ const FilterItem = ({
}) => (
<Box sx={{ p: 2 }}>
<Typography gutterBottom>{label}</Typography>
<Typography fontSize={12}>{tooltipInfo}</Typography>
<Slider
value={value}
onChange={(e: Event, newValue: number | number[]) => onChange(id, newValue as number[])}
@@ -50,7 +51,7 @@ const FilterItem = ({
);
export const Filters = () => {
const { filterMixnodes, fetchMixnodes } = useMainContext();
const { filterMixnodes, fetchMixnodes, mixnodes } = useMainContext();
const { status } = useParams<{ status: MixnodeStatusWithAll | undefined }>();
const isMobile = useIsMobile();
@@ -129,17 +130,23 @@ export const Filters = () => {
variant={isMobile ? 'standard' : 'outlined'}
action={
<Button size="small" onClick={onClearFilters}>
Clear
CLEAR FILTERS
</Button>
}
sx={{ width: 300 }}
>
Filters applied
{mixnodes?.data?.length} mixnodes matched your criteria
</Alert>
</Snackbar>
<IconButton size="large" onClick={handleToggleShowFilters}>
<Tune />
</IconButton>
<Button
size="large"
variant="text"
color="inherit"
endIcon={<Tune />}
onClick={handleToggleShowFilters}
sx={{ textTransform: 'none' }}
>
Advanced filters
</Button>
<Dialog open={showFilters} onClose={handleToggleShowFilters} maxWidth="md" fullWidth>
<DialogTitle>Mixnode filters</DialogTitle>
<DialogContent dividers>
+20 -54
View File
@@ -18,6 +18,8 @@ export const generateFilterSchema = (upperSaturationValue?: number) => ({
{ label: '90', value: 90 },
{ label: '100', value: 100 },
],
tooltipInfo:
'As a delegator you want to chose nodes with lower profit margin, meaning more payout for their delegators',
},
stakeSaturation: {
label: 'Stake saturation (%)',
@@ -43,65 +45,29 @@ export const generateFilterSchema = (upperSaturationValue?: number) => ({
},
],
max: upperSaturationValue,
tooltipInfo: "Select nodes with <100% saturation. Any additional stake above 100% saturation won't get rewards",
},
stake: {
label: 'Stake (NYM)',
id: EnumFilterKey.stake,
value: [20, 90],
min: 20,
max: 90,
routingScore: {
label: 'Routing score (%)',
id: EnumFilterKey.routingScore,
value: [0, 100],
marks: [
{
value: 0,
label: '1',
},
{
value: 10,
label: '10',
},
{
value: 20,
label: '100',
},
{
value: 30,
label: '1k',
},
{
value: 40,
label: '10k',
},
{
value: 50,
label: '100k',
},
{
value: 60,
label: '1M',
},
{
value: 70,
label: '10M',
},
{
value: 80,
label: '100M',
},
{
value: 90,
label: '1B',
},
{ label: '0', value: 0 },
{ label: '10', value: 10 },
{ label: '20', value: 20 },
{ label: '30', value: 30 },
{ label: '40', value: 40 },
{ label: '50', value: 50 },
{ label: '60', value: 60 },
{ label: '70', value: 70 },
{ label: '80', value: 80 },
{ label: '90', value: 90 },
{ label: '100', value: 100 },
],
tooltipInfo: 'The higher the routing score the better the performance of the node and so its rewards',
},
});
const formatStakeValuesToMinorDenom = ([value_1, value_2]: number[]) => {
const lowerValue = 10 ** (value_1 / 10) * 1_000_000;
const upperValue = 10 ** (value_2 / 10) * 1_000_000;
return [lowerValue, upperValue];
};
const formatStakeSaturationValues = ([value_1, value_2]: number[]) => {
const lowerValue = value_1 / 100;
const upperValue = value_2 / 100;
@@ -110,7 +76,7 @@ const formatStakeSaturationValues = ([value_1, value_2]: number[]) => {
};
export const formatOnSave = (filters: TFilters) => ({
stake: formatStakeValuesToMinorDenom(filters.stake.value),
routingScore: filters.routingScore.value,
profitMargin: filters.profitMargin.value,
stakeSaturation: formatStakeSaturationValues(filters.stakeSaturation.value),
});
+2 -2
View File
@@ -102,8 +102,8 @@ export const MainContextProvider: React.FC = ({ children }) => {
m.mix_node.profit_margin_percent <= filters.profitMargin[1] &&
m.stake_saturation >= filters.stakeSaturation[0] &&
m.stake_saturation <= filters.stakeSaturation[1] &&
+m.pledge_amount.amount + +m.total_delegation.amount >= filters.stake[0] &&
+m.pledge_amount.amount + +m.total_delegation.amount <= filters.stake[1],
m.avg_uptime >= filters.routingScore[0] &&
m.avg_uptime <= filters.routingScore[1],
);
setMixnodes({ data: filtered, isLoading: false });
};
+2 -1
View File
@@ -4,7 +4,7 @@ import { Mark } from '@mui/base';
export enum EnumFilterKey {
profitMargin = 'profitMargin',
stakeSaturation = 'stakeSaturation',
stake = 'stake',
routingScore = 'routingScore',
}
export type TFilterItem = {
@@ -15,6 +15,7 @@ export type TFilterItem = {
min?: number;
max?: number;
scale?: (value: number) => number;
tooltipInfo?: string;
};
export type TFilters = { [key in EnumFilterKey]: TFilterItem };
@@ -6,7 +6,15 @@ import { AccountAvatar } from './AccountAvatar';
export const AccountOverview = ({ account, onClick }: { account: AccountEntry; onClick: () => void }) => (
<Button
startIcon={<AccountAvatar name={account.id} />}
sx={{ color: 'text.primary' }}
sx={{
color: 'text.primary',
'&:hover': (t) =>
t.palette.mode === 'dark'
? {
backgroundColor: 'rgba(255, 255, 255, 0.08)',
}
: {},
}}
onClick={onClick}
disableRipple
>
@@ -10,9 +10,9 @@ export default {
const Template: ComponentStory<typeof ConfirmTx> = (args) => (
<ConfirmTx {...args}>
<ModalListItem label="Transaction type" value="Bond" divider />
<ModalListItem label="Current bond" value="100 NYM" divider />
<ModalListItem label="Additional bond" value="50 NYM" divider />
<ModalListItem label="Transaction type:" value="Bond" divider />
<ModalListItem label="Current bond:" value="100 NYM" divider />
<ModalListItem label="Additional bond:" value="50 NYM" divider />
</ConfirmTx>
);
@@ -168,8 +168,8 @@ export const DelegateModal: React.FC<{
onPrev={resetFeeState}
onConfirm={handleOk}
>
<ModalListItem label="Node identity key" value={identityKey} divider />
<ModalListItem label="Amount" value={`${amount} ${denom.toUpperCase()}`} divider />
<ModalListItem label="Node identity key:" value={identityKey} divider />
<ModalListItem label="Amount:" value={`${amount} ${denom.toUpperCase()}`} divider />
</ConfirmTx>
);
}
@@ -184,23 +184,24 @@ export const DelegateModal: React.FC<{
}
}}
header={header || 'Delegate'}
subHeader="Delegate to mixnode"
okLabel={buttonText || 'Delegate stake'}
okDisabled={!isValidated}
sx={sx}
backdropProps={backdropProps}
>
<IdentityKeyFormField
required
fullWidth
placeholder="Node identity key"
onChanged={handleIdentityKeyChanged}
initialValue={identityKey}
readOnly={Boolean(initialIdentityKey)}
textFieldProps={{
autoFocus: !initialIdentityKey,
}}
/>
<Box sx={{ mt: 2 }}>
<IdentityKeyFormField
required
fullWidth
placeholder="Node identity key"
onChanged={handleIdentityKeyChanged}
initialValue={identityKey}
readOnly={Boolean(initialIdentityKey)}
textFieldProps={{
autoFocus: !initialIdentityKey,
}}
/>
</Box>
<Typography
component="div"
textAlign="left"
@@ -230,29 +231,30 @@ export const DelegateModal: React.FC<{
{errorAmount}
</Typography>
<Box sx={{ mt: 3 }}>
<ModalListItem label="Account balance" value={accountBalance} divider />
<ModalListItem label="Account balance:" value={accountBalance} divider strong />
</Box>
<ModalListItem label="Rewards payout interval" value={rewardInterval} hidden divider />
<ModalListItem label="Rewards payout interval:" value={rewardInterval} hidden divider />
<ModalListItem
label="Node profit margin"
label="Node profit margin:"
value={`${profitMarginPercentage ? `${profitMarginPercentage}%` : '-'}`}
hidden={profitMarginPercentage === undefined}
divider
/>
<ModalListItem
label="Node uptime"
label="Node avg. uptime:"
value={`${nodeUptimePercentage ? `${nodeUptimePercentage}%` : '-'}`}
hidden={nodeUptimePercentage === undefined}
divider
/>
<ModalListItem
label="Node est. reward per epoch"
label="Node est. reward per epoch:"
value={`${estimatedReward} ${denom.toUpperCase()}`}
hidden
divider
/>
<ModalListItem label="Est. fee for this transaction will be calculated in the next page" />
</SimpleModal>
);
};
@@ -29,9 +29,6 @@ const transactionForDarkTheme = {
url: 'https://sandbox-blocks.nymtech.net/transactions/11ED7B9E21534A9421834F52FED5103DC6E982949C06335F5E12EFC71DAF0CFO',
hash: '11ED7B9E21534A9421834F52FED5103DC6E982949C06335F5E12EFC71DAF0CF0',
};
const balance = '104 NYMT';
const balanceVested = '12 NYMT';
const recipient = 'nymt1923pujepxfnv8dqyxqrl078s4ysf3xn2p7z2xa';
const Content: React.FC<{ children: React.ReactElement<any, any>; handleClick: () => void }> = ({
children,
@@ -78,8 +75,6 @@ export const DelegateSuccess = () => {
status="success"
action="delegate"
message="You delegated 5 NYM"
recipient={recipient}
balance={balance}
transactions={theme.palette.mode === 'light' ? [transaction] : [transactionForDarkTheme]}
{...storybookStyles(theme)}
/>
@@ -99,8 +94,6 @@ export const UndelegateSuccess = () => {
status="success"
action="undelegate"
message="You undelegated 5 NYM"
recipient={recipient}
balance={balance}
transactions={theme.palette.mode === 'light' ? [transaction] : [transactionForDarkTheme]}
{...storybookStyles(theme)}
/>
@@ -120,8 +113,6 @@ export const RedeemSuccess = () => {
status="success"
action="redeem"
message="42 NYM"
recipient={recipient}
balance={balance}
transactions={
theme.palette.mode === 'light'
? [transaction, transaction]
@@ -145,9 +136,6 @@ export const RedeemWithVestedSuccess = () => {
status="success"
action="redeem"
message="42 NYM"
recipient={recipient}
balance={balance}
balanceVested={balanceVested}
transactions={
theme.palette.mode === 'light'
? [transaction, transaction]
@@ -171,8 +159,6 @@ export const RedeemAllSuccess = () => {
status="success"
action="redeem-all"
message="42 NYM"
recipient={recipient}
balance={balance}
transactions={
theme.palette.mode === 'light'
? [transaction, transaction]
@@ -196,8 +182,6 @@ export const Error = () => {
status="error"
action="redeem-all"
message="Minim esse veniam Lorem id velit Lorem eu eu est. Excepteur labore sunt do proident proident sint aliquip consequat Lorem sint non nulla ad excepteur."
recipient={recipient}
balance={balance}
transactions={theme.palette.mode === 'light' ? [transaction] : [transactionForDarkTheme]}
{...storybookStyles(theme)}
/>
@@ -1,9 +1,10 @@
import React from 'react';
import { Box, Button, Modal, Typography, SxProps } from '@mui/material';
import { Box, Button, Modal, Typography, SxProps, Stack } from '@mui/material';
import { Link } from '@nymproject/react/link/Link';
import { Console } from 'src/utils/console';
import { modalStyle } from '../Modals/styles';
import { LoadingModal } from '../Modals/LoadingModal';
import { ConfirmationModal } from '../Modals/ConfirmationModal';
export type ActionType = 'delegate' | 'undelegate' | 'redeem' | 'redeem-all' | 'compound';
@@ -15,9 +16,9 @@ const actionToHeader = (action: ActionType): string => {
case 'redeem-all':
return 'All rewards redeemed successfully';
case 'delegate':
return 'Delegation complete';
return 'Delegation successful';
case 'undelegate':
return 'Undelegation complete';
return 'Undelegation successful';
case 'compound':
return 'Rewards compounded successfully';
default:
@@ -29,9 +30,6 @@ export type DelegationModalProps = {
status: 'loading' | 'success' | 'error';
action: ActionType;
message?: string;
recipient?: string;
balance?: string;
balanceVested?: string;
transactions?: {
url: string;
hash: string;
@@ -45,20 +43,7 @@ export const DelegationModal: React.FC<
sx?: SxProps;
backdropProps?: object;
}
> = ({
status,
action,
message,
recipient,
balance,
balanceVested,
transactions,
open,
onClose,
children,
sx,
backdropProps,
}) => {
> = ({ status, action, message, transactions, open, onClose, children, sx, backdropProps }) => {
if (status === 'loading') return <LoadingModal sx={sx} backdropProps={backdropProps} />;
if (status === 'error') {
@@ -81,54 +66,28 @@ export const DelegationModal: React.FC<
}
transactions?.map((transaction) => Console.log('action', action, 'status', status, 'key', transaction.hash));
return (
<Modal open={open} onClose={onClose} BackdropProps={backdropProps}>
<Box sx={{ ...modalStyle, ...sx }} textAlign="center">
<Typography color={(theme) => theme.palette.success.main} mb={1}>
{actionToHeader(action)}
</Typography>
<Typography mb={3} color="text.primary">
{message}
</Typography>
{recipient && (
<Typography mb={1} fontSize="small" color={(theme) => theme.palette.text.secondary}>
Recipient: {recipient}
</Typography>
return (
<ConfirmationModal
open={open}
onConfirm={onClose || (() => {})}
title={actionToHeader(action)}
confirmButton="Done"
>
<Stack alignItems="center" spacing={2} mb={0}>
{message && <Typography>{message}</Typography>}
{transactions?.length === 1 && (
<Link href={transactions[0].url} target="_blank" sx={{ ml: 1 }} text="View on blockchain" noIcon />
)}
{balanceVested ? (
<>
<Typography mb={1} fontSize="small" color={(theme) => theme.palette.text.secondary}>
Your current balance: {balance?.toUpperCase()}
</Typography>
<Typography mb={1} fontSize="small" color={(theme) => theme.palette.text.secondary}>
({balanceVested.toUpperCase()} is unlocked in your vesting account)
</Typography>
</>
) : (
<Typography mb={1} fontSize="small" color={(theme) => theme.palette.text.secondary}>
Your current balance: {balance?.toUpperCase()}
</Typography>
)}
{transactions && (
<Typography mb={1} fontSize="small" color={(theme) => theme.palette.text.secondary}>
Check the transaction {transactions.length > 1 ? 'hashes' : 'hash'}:
{transactions.map((transaction) => (
<Link
key={transaction.hash}
href={transaction.url}
target="_blank"
sx={{ ml: 1 }}
text={transaction.hash.slice(0, 6)}
/>
{transactions && transactions.length > 1 && (
<Stack alignItems="center" spacing={1}>
<Typography>View the transactions on blockchain:</Typography>
{transactions.map(({ url, hash }) => (
<Link href={url} target="_blank" sx={{ ml: 1 }} text={hash.slice(0, 6)} key={hash} noIcon />
))}
</Typography>
</Stack>
)}
{children}
<Button variant="contained" sx={{ mt: 3 }} size="large" onClick={onClose}>
Finish
</Button>
</Box>
</Modal>
</Stack>
</ConfirmationModal>
);
};
@@ -55,7 +55,7 @@ export const UndelegateModal: React.FC<{
/>
<Box sx={{ mt: 3 }}>
<ModalListItem label="Delegation amount" value={`${amount} ${currency}`} divider />
<ModalListItem label="Delegation amount:" value={`${amount} ${currency}`} divider />
</Box>
<Typography mb={5} fontSize="smaller" sx={{ color: 'text.primary' }}>
@@ -13,5 +13,5 @@ const getValue = ({ fee, isLoading, error }: TFeeProps) => {
};
export const ModalFee = ({ fee, isLoading, error }: TFeeProps) => (
<ModalListItem label="Estimated fee for this operation" value={getValue({ fee, isLoading, error })} />
<ModalListItem label="Estimated fee for this operation:" value={getValue({ fee, isLoading, error })} />
);
@@ -7,20 +7,22 @@ export const ModalListItem: React.FC<{
divider?: boolean;
hidden?: boolean;
strong?: boolean;
value: React.ReactNode;
value?: React.ReactNode;
}> = ({ label, value, hidden, divider, strong }) => (
<Box sx={{ display: hidden ? 'none' : 'block' }}>
<Stack direction="row" justifyContent="space-between">
<Typography fontSize="smaller" fontWeight={strong ? 600 : undefined} sx={{ color: 'text.primary' }}>
{label}:
</Typography>
<Typography
fontSize="smaller"
fontWeight={strong ? 600 : undefined}
sx={{ color: 'text.primary', textTransform: 'uppercase' }}
>
{value}
{label}
</Typography>
{value && (
<Typography
fontSize="smaller"
fontWeight={strong ? 600 : undefined}
sx={{ color: 'text.primary', textTransform: 'uppercase' }}
>
{value}
</Typography>
)}
</Stack>
{divider && <ModalDivider />}
</Box>
+28 -3
View File
@@ -68,9 +68,16 @@ export const Nav = () => {
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-start',
marginLeft: 12,
marginRight: 12,
}}
>
<List disablePadding>
<List
disablePadding
sx={{
width: '100%',
}}
>
{routesSchema
.filter(({ mode }) => {
if (!mode) {
@@ -86,17 +93,35 @@ export const Nav = () => {
}
})
.map(({ Icon, onClick, label, route }) => (
<ListItem disableGutters key={label} onClick={onClick} sx={{ cursor: 'pointer' }}>
<ListItem
disableGutters
key={label}
onClick={onClick}
sx={{
cursor: 'pointer',
py: 2,
paddingLeft: 3.5,
borderRadius: 1,
'&:hover': { backgroundColor: (theme) => theme.palette.nym.nymWallet.hover.background },
}}
>
<ListItemIcon
sx={{
height: '20px',
minWidth: 30,
color: location.pathname === route ? 'primary.main' : 'text.primary',
}}
>
<Icon sx={{ fontSize: 20 }} />
<Icon
sx={{
fontSize: 20,
}}
/>
</ListItemIcon>
<ListItemText
sx={{
height: '20px',
margin: 0,
color: location.pathname === route ? 'primary.main' : 'text.primary',
'& .MuiListItemText-primary': {
fontSize: 14,
+10 -1
View File
@@ -40,7 +40,16 @@ export const NetworkSelector = () => {
<Button
variant="text"
color="primary"
sx={{ color: 'text.primary' }}
sx={{
color: 'text.primary',
fontSize: 14,
'&:hover': (t) =>
t.palette.mode === 'dark'
? {
backgroundColor: 'rgba(255, 255, 255, 0.08)',
}
: {},
}}
onClick={handleClick}
disableElevation
endIcon={<ArrowDropDown sx={{ color: (theme) => `1px solid ${theme.palette.text.primary}` }} />}
+4 -2
View File
@@ -1,4 +1,5 @@
import React from 'react';
import { SxProps } from '@mui/material';
import { Box, Card, CardContent, CardHeader } from '@mui/material';
import { styled, Theme } from '@mui/material/styles';
import { Title } from './Title';
@@ -12,16 +13,17 @@ const CardContentNoPadding = styled(CardContent)(() => ({
export const NymCard: React.FC<{
title: string | React.ReactElement;
titleSx?: SxProps;
subheader?: string;
Action?: React.ReactNode;
Icon?: React.ReactNode;
noPadding?: boolean;
borderless?: boolean;
dataTestid?: string;
}> = ({ title, subheader, Action, Icon, noPadding, borderless, children, dataTestid }) => (
}> = ({ title, titleSx, subheader, Action, Icon, noPadding, borderless, children, dataTestid }) => (
<Card variant="outlined" sx={{ overflow: 'auto', ...(borderless && { border: 'none', dropShadow: 'none' }) }}>
<CardHeader
sx={{ p: 3, color: (theme: Theme) => theme.palette.text.primary }}
sx={{ p: 3, color: (theme: Theme) => theme.palette.text.primary, ...titleSx }}
title={<Title title={title} Icon={Icon} />}
subheader={subheader}
data-testid={dataTestid || title}
@@ -39,11 +39,11 @@ export const SendDetailsModal = ({
backdropProps={backdropProps}
>
<Stack gap={0.5} sx={{ mt: 4 }}>
<ModalListItem label="From" value={fromAddress} divider />
<ModalListItem label="To" value={toAddress} divider />
<ModalListItem label="Amount" value={`${amount?.amount} ${denom.toUpperCase()}`} divider />
<ModalListItem label="From:" value={fromAddress} divider />
<ModalListItem label="To:" value={toAddress} divider />
<ModalListItem label="Amount:" value={`${amount?.amount} ${denom.toUpperCase()}`} divider />
<ModalListItem
label="Fee for this transaction"
label="Fee for this transaction:"
value={!fee ? 'n/a' : `${fee.amount?.amount} ${fee.amount?.denom}`}
divider
/>
@@ -77,8 +77,8 @@ export const SendInputModal = ({
</Typography>
</Stack>
<Stack gap={0.5} sx={{ mt: 2 }}>
<ModalListItem label="Account balance" value={balance} divider strong />
<ModalListItem label="Your address" value={fromAddress} divider />
<ModalListItem label="Account balance:" value={balance} divider strong />
<ModalListItem label="Your address:" value={fromAddress} divider />
<Typography fontSize="smaller" sx={{ color: 'text.primary' }}>
Est. fee for this transaction will be show on the next page
</Typography>
+4 -5
View File
@@ -26,26 +26,25 @@ export const ApplicationLayout: React.FC = ({ children }) => {
sx={{
background: (t) => t.palette.nym.nymWallet.nav.background,
overflow: 'auto',
py: 3,
px: 5,
py: 5,
}}
display="flex"
flexDirection="column"
justifyContent="space-between"
>
<Box>
<Box sx={{ mb: 4 }}>
<Box sx={{ ml: 5, mb: 3 }}>
<NymWordmark height={14} />
</Box>
<Nav />
</Box>
{appVersion && (
<Box color="#888" mt={8}>
<Box color="#888" ml={5} mt={8} fontSize={14}>
Version {appVersion}
</Box>
)}
</Box>
<Container maxWidth="xl">
<Container maxWidth="xl" sx={{ px: { sm: 5 } }}>
<AppBar />
<Box overflow="auto" sx={{ height: () => `calc(100% - ${theme.spacing(10)})` }}>
{children}
+1
View File
@@ -14,6 +14,7 @@ export const BalanceCard = () => {
return (
<NymCard
title="Balance"
titleSx={{ fontSize: 20 }}
data-testid="check-balance"
borderless
Action={<ClientAddress withCopy showEntireAddress />}
@@ -87,12 +87,12 @@ export const TransferModal = ({ onClose }: { onClose: () => void }) => {
) : (
<>
<ModalListItem
label="Unlocked transferrable tokens"
label="Unlocked transferrable tokens:"
value={`${userBalance.tokenAllocation?.spendable} ${clientDetails?.display_mix_denom.toUpperCase()}`}
divider
/>
<ModalListItem
label="Est. fee for this transaction"
label="Est. fee for this transaction:"
value={fee ? `${fee.amount?.amount} ${fee.amount?.denom}` : <CircularProgress size={15} />}
divider
/>
@@ -1,5 +1,6 @@
/* eslint-disable react/no-array-index-key */
import React, { useContext } from 'react';
import { useTheme } from '@mui/material/styles';
import { Box, Tooltip, Typography } from '@mui/material';
import { format } from 'date-fns';
import { AppContext } from '../../../context/main';
@@ -21,6 +22,9 @@ export const VestingTimeline: React.FC<{ percentageComplete: number }> = ({ perc
userBalance: { currentVestingPeriod, vestingAccountInfo },
} = useContext(AppContext);
const theme = useTheme();
const { mode } = theme.palette;
const nextPeriod =
typeof currentVestingPeriod === 'object' && !!vestingAccountInfo?.periods
? Number(vestingAccountInfo?.periods[currentVestingPeriod.In + 1]?.start_time)
@@ -29,19 +33,31 @@ export const VestingTimeline: React.FC<{ percentageComplete: number }> = ({ perc
return (
<Box display="flex" flexDirection="column" gap={1} position="relative" width="100%">
<svg width="100%" height="12">
<rect y="2" width="100%" height="6" rx="0" fill="#E6E6E6" />
<rect y="2" width={`${percentageComplete}%`} height="6" rx="0" fill="#121726" />
<rect y="2" width="100%" height="6" rx="0" fill="text.dark" />
<rect
y="2"
width={`${percentageComplete}%`}
height="6"
rx="0"
fill={mode === 'light' ? 'text.dark' : theme.palette.success.main}
/>
{vestingAccountInfo?.periods.map((period, i, arr) => (
<Marker
position={`${calculateMarkerPosition(arr.length, i)}%`}
color={+percentageComplete.toFixed(2) >= calculateMarkerPosition(arr.length, i) ? '#121726' : '#B9B9B9'}
color={
+percentageComplete.toFixed(2) >= calculateMarkerPosition(arr.length, i)
? mode === 'light'
? 'text.dark'
: theme.palette.success.main
: '#B9B9B9'
}
tooltipText={format(new Date(Number(period.start_time) * 1000), 'HH:mm do MMM yyyy')}
key={i}
/>
))}
<Marker
position="calc(100% - 4px)"
color={percentageComplete === 100 ? '#121726' : '#B9B9B9'}
color={percentageComplete === 100 ? (mode === 'light' ? 'text.dark' : theme.palette.success.main) : '#B9B9B9'}
tooltipText="End of vesting schedule"
/>
</svg>
+28 -4
View File
@@ -65,20 +65,44 @@ const VestingSchedule = () => {
))}
</TableRow>
<TableRow>
<TableCell sx={{ borderBottom: 'none', textTransform: 'uppercase' }}>
<TableCell
sx={{
color: (t) => (t.palette.mode === 'light' ? t.palette.nym.text.muted : 'text.primary'),
borderBottom: 'none',
textTransform: 'uppercase',
}}
>
{userBalance.tokenAllocation?.vesting || 'n/a'} / {userBalance.originalVesting?.amount.amount}{' '}
{clientDetails?.display_mix_denom.toUpperCase()}
</TableCell>
<TableCell align="left" sx={{ borderBottom: 'none' }}>
<TableCell
align="left"
sx={{
color: (t) => (t.palette.mode === 'light' ? t.palette.nym.text.muted : 'text.primary'),
borderBottom: 'none',
}}
>
{vestingPeriod(userBalance.currentVestingPeriod, userBalance.originalVesting?.number_of_periods)}
</TableCell>
<TableCell sx={{ borderBottom: 'none' }}>
<TableCell
sx={{
color: (t) => (t.palette.mode === 'light' ? t.palette.nym.text.muted : 'text.primary'),
borderBottom: 'none',
}}
>
<Box display="flex" alignItems="center" gap={1}>
<Typography variant="body2">{`${vestedPercentage}%`}</Typography>
<VestingTimeline percentageComplete={vestedPercentage} />
</Box>
</TableCell>
<TableCell sx={{ borderBottom: 'none', textTransform: 'uppercase' }} align="right">
<TableCell
sx={{
color: (t) => (t.palette.mode === 'light' ? t.palette.nym.text.muted : 'text.primary'),
borderBottom: 'none',
textTransform: 'uppercase',
}}
align="right"
>
{userBalance.tokenAllocation?.vested || 'n/a'} / {userBalance.originalVesting?.amount.amount}{' '}
{clientDetails?.display_mix_denom.toUpperCase()}
</TableCell>
@@ -20,8 +20,8 @@ export const ConfirmationModal = ({
}) => (
<SimpleModal header="Bond confirmation" open onOk={onConfirm} okLabel="Confirm" hideCloseIcon onBack={onPrev}>
<Box sx={{ mt: 3 }}>
<ModalListItem label="Mixnode identity" value={identity} />
<ModalListItem label="Amount" value={`${amount.amount} ${amount.denom}`} />
<ModalListItem label="Mixnode identity:" value={identity} />
<ModalListItem label="Amount:" value={`${amount.amount} ${amount.denom}`} />
<ModalFee fee={fee} isLoading={false} />
</Box>
</SimpleModal>
+1 -5
View File
@@ -138,7 +138,7 @@ export const Delegation: FC<{ isStorybook?: boolean }> = ({ isStorybook }) => {
setConfirmationModalProps({
status: 'success',
action: 'delegate',
message: 'Delegations can take up to one hour to process',
message: 'This operation can take up to one hour to process',
...balances,
transactions: [
{ url: `${urls(network).blockExplorer}/transaction/${tx.transaction_hash}`, hash: tx.transaction_hash },
@@ -240,11 +240,9 @@ export const Delegation: FC<{ isStorybook?: boolean }> = ({ isStorybook }) => {
try {
const txs = await claimRewards(identityKey, fee);
const bal = await userBalance();
setConfirmationModalProps({
status: 'success',
action: 'redeem',
balance: bal?.printable_balance || '-',
transactions: txs.map((tx) => ({
url: `${urls(network).blockExplorer}/transaction/${tx.transaction_hash}`,
hash: tx.transaction_hash,
@@ -270,11 +268,9 @@ export const Delegation: FC<{ isStorybook?: boolean }> = ({ isStorybook }) => {
try {
const txs = await compoundRewards(identityKey, fee);
const bal = await userBalance();
setConfirmationModalProps({
status: 'success',
action: 'compound',
balance: bal?.printable_balance || '-',
transactions: txs.map((tx) => ({
url: `${urls(network).blockExplorer}/transaction/${tx.transaction_hash}`,
hash: tx.transaction_hash,
+8 -5
View File
@@ -64,6 +64,9 @@ declare module '@mui/material/styles' {
nav: {
background: string;
};
hover: {
background: string;
};
}
/**
@@ -82,7 +85,7 @@ declare module '@mui/material/styles' {
/**
* Add anything not palette related to the theme here
*/
interface NymTheme {}
interface NymTheme { }
/**
* This augments the definitions of the MUI Theme with the Nym theme, as well as
@@ -90,8 +93,8 @@ declare module '@mui/material/styles' {
*
* IMPORTANT: only add extensions to the interfaces above, do not modify the lines below
*/
interface Theme extends NymTheme {}
interface ThemeOptions extends Partial<NymTheme> {}
interface Palette extends NymPaletteAndNymWalletPalette {}
interface PaletteOptions extends NymPaletteAndNymWalletPaletteOptions {}
interface Theme extends NymTheme { }
interface ThemeOptions extends Partial<NymTheme> { }
interface Palette extends NymPaletteAndNymWalletPalette { }
interface PaletteOptions extends NymPaletteAndNymWalletPaletteOptions { }
}
+7 -1
View File
@@ -56,12 +56,15 @@ const darkMode: NymPaletteVariant = {
nav: {
background: '#292E34',
},
hover: {
background: '#36393E',
},
};
const lightMode: NymPaletteVariant = {
mode: 'light',
background: {
main: '#E5E5E5',
main: '#F4F6F8',
paper: '#FFFFFF',
warn: '#FFE600',
grey: '#F5F5F5',
@@ -80,6 +83,9 @@ const lightMode: NymPaletteVariant = {
nav: {
background: '#FFFFFF',
},
hover: {
background: '#F9F9F9',
},
};
/**
-29
View File
@@ -1,29 +0,0 @@
[package]
name = "validator-client-scripts"
version = "0.1.0"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
base64 = "0.13.0"
bs58 = "0.4"
clap = { version = "3.2.8", features = ["derive"] }
csv = "1.1"
dotenv = "0.15.0"
log = "0.4"
pretty_env_logger = "0.4"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tokio = { version = "1.11", features = [ "net", "rt-multi-thread", "macros", "signal"] }
streaming-stats = "0.2.3"
# we don't really need those directly, but including them somehow solves dependency issue
url = "2.2"
bip39 = "1.0.1"
validator-client = { path = "../../common/client-libs/validator-client", features = ["nymd-client"] }
network-defaults = { path = "../../common/network-defaults" }
mixnet-contract-common ={ path = "../../common/cosmwasm-smart-contracts/mixnet-contract" }
vesting-contract-common = { path = "../../common/cosmwasm-smart-contracts/vesting-contract" }
-8
View File
@@ -1,8 +0,0 @@
install:
cargo install --path .
clean:
cargo clean
fmt:
cargo fmt --all
-32
View File
@@ -1,32 +0,0 @@
# NYM - CLI TOOL
## Overview
Set of simple CLI commands to perform basic actions, such as upload contract, bond mixnode, etc.
Note: to work it requires rust 1.60+
## The binary
- Build the binary
- cargo build --release
- this will compile the binary to "your/main/nym/path/target/release"
- the binary is named ./validator-client-scripts-rs
## Executing commands
-./validator-scripts-rs --help
- will produce a list of it's capabilites
The binary requires mandatory OPTIONS:
```
--config-env-file <CONFIG_ENV_FILE>
--mixnet-contract <MIXNET_CONTRACT>
--mnemonic <MNEMONIC>
--nymd-url <NYMD_URL>
--vesting-contract <VESTING_CONTRACT>
```
If you specify --config-env-file it will read the values from the envs/directory:
`./validator-client-scripts-rs --config-env-file env/qa.env .....` and you don't need to supply the
mixnet-contract nor the vesting-contract argument
An example of a command is as follows:
```
Disclaimer the amount is in UNYMs
./validator-client-scripts --config-env-file ../../envs/qa.env --nymd-url https://qa-validator.nymtech.net --mnemonic "INPUT YOUR MNEMONIC" send --amount 100000000 --recipient <NYM_ADDRESS>
```
@@ -1,20 +0,0 @@
CONFIGURED=true
RUST_LOG=info
RUST_BACKTRACE=1
BECH32_PREFIX=n
MIX_DENOM=unym
MIX_DENOM_DISPLAY=nym
STAKE_DENOM=unyx
STAKE_DENOM_DISPLAY=nyx
DENOMS_EXPONENT=6
MIXNET_CONTRACT_ADDRESS=n14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sjyvg3g
VESTING_CONTRACT_ADDRESS=n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw
BANDWIDTH_CLAIM_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0
COCONUT_BANDWIDTH_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0
MULTISIG_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0
REWARDING_VALIDATOR_ADDRESS=n1ms55meaqd3ztytstwn0v0pl7kjrgmnq6d0674t
STATISTICS_SERVICE_DOMAIN_ADDRESS="http://127.0.0.1:8090"
NYMD_VALIDATOR="http://validator-service:26657"
API_VALIDATOR="http://validator-api-service:8080"
@@ -1,20 +0,0 @@
CONFIGURED=true
RUST_LOG=info
RUST_BACKTRACE=1
BECH32_PREFIX=n
MIX_DENOM=unym
MIX_DENOM_DISPLAY=nym
STAKE_DENOM=unyx
STAKE_DENOM_DISPLAY=nyx
DENOMS_EXPONENT=6
MIXNET_CONTRACT_ADDRESS=n14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sjyvg3g
VESTING_CONTRACT_ADDRESS=n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw
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"
NYMD_VALIDATOR="https://rpc.nyx.nodes.guru/"
API_VALIDATOR="https://validator.nymtech.net/api/"
@@ -1,20 +0,0 @@
CONFIGURED=true
RUST_LOG=info
RUST_BACKTRACE=1
BECH32_PREFIX=n
MIX_DENOM=unym
MIX_DENOM_DISPLAY=nym
STAKE_DENOM=unyx
STAKE_DENOM_DISPLAY=nyx
DENOMS_EXPONENT=6
MIXNET_CONTRACT_ADDRESS=n1suhgf5svhu4usrurvxzlgn54ksxmn8gljarjtxqnapv8kjnp4nrsd3qaep
VESTING_CONTRACT_ADDRESS=n1xr3rq8yvd7qplsw5yx90ftsr2zdhg4e9z60h5duusgxpv72hud3sjkxkav
BANDWIDTH_CLAIM_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0
COCONUT_BANDWIDTH_CONTRACT_ADDRESS=n1ghd753shjuwexxywmgs4xz7x2q732vcn7ty4yw
MULTISIG_CONTRACT_ADDRESS=n17p9rzwnnfxcjp32un9ug7yhhzgtkhvl988qccs
REWARDING_VALIDATOR_ADDRESS=n1tfzd4qz3a45u8p4mr5zmzv66457uwjgcl05jdq
STATISTICS_SERVICE_DOMAIN_ADDRESS="http://0.0.0.0"
NYMD_VALIDATOR="https://qa-validator.nymtech.net"
API_VALIDATOR="https://qa-validator-api.nymtech.net/api"
@@ -1,78 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::Client;
use clap::Parser;
use log::{info, warn};
use mixnet_contract_common::Coin;
use network_defaults::{DEFAULT_CLIENT_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT};
#[derive(Debug, Parser)]
pub(crate) struct Args {
#[clap(long)]
pub host: String,
#[clap(long)]
pub signature: String,
#[clap(long)]
pub mix_port: Option<u16>,
#[clap(long)]
pub clients_port: Option<u16>,
#[clap(long)]
pub location: Option<String>,
#[clap(long)]
pub sphinx_key: String,
#[clap(long)]
pub identity_key: String,
#[clap(long)]
pub version: String,
#[clap(
long,
help = "bonding amount in current DENOMINATION (so it would be 'unym', rather than 'nym')"
)]
pub amount: u128,
#[clap(long)]
pub gas: Option<u64>,
#[clap(short, long)]
pub force: bool,
}
pub(crate) async fn bond_gateway(client: Client, args: Args, denom: &str) {
info!("Starting gateway bonding!");
// if we're trying to bond less than 1 token
if args.amount < 1_000_000 && !args.force {
warn!("You're trying to bond only {}{} which is less than 1 full token. Are you sure that's what you want? If so, run with `--force` or `-f` flag", args.amount, denom);
return;
}
let gateway = mixnet_contract_common::Gateway {
host: args.host,
mix_port: args.mix_port.unwrap_or(DEFAULT_MIX_LISTENING_PORT),
clients_port: args.clients_port.unwrap_or(DEFAULT_CLIENT_LISTENING_PORT),
location: args
.location
.unwrap_or_else(|| "secret gateway location".to_owned()),
sphinx_key: args.sphinx_key,
identity_key: args.identity_key,
version: args.version,
};
let coin = Coin::new(args.amount, denom);
let res = client
.bond_gateway(gateway, args.signature, coin.into(), None)
.await
.expect("failed to bond gateway!");
info!("Bonding result: {:?}", res)
}
@@ -1,84 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::Client;
use clap::Parser;
use log::{info, warn};
use mixnet_contract_common::Coin;
use network_defaults::{
DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT,
};
#[derive(Debug, Parser)]
pub(crate) struct Args {
#[clap(long)]
pub host: String,
#[clap(long)]
pub signature: String,
#[clap(long)]
pub mix_port: Option<u16>,
#[clap(long)]
pub verloc_port: Option<u16>,
#[clap(long)]
pub http_api_port: Option<u16>,
#[clap(long)]
pub sphinx_key: String,
#[clap(long)]
pub identity_key: String,
#[clap(long)]
pub version: String,
#[clap(long)]
pub profit_margin_percent: Option<u8>,
#[clap(
long,
help = "bonding amount in current DENOMINATION (so it would be 'unym', rather than 'nym')"
)]
pub amount: u128,
#[clap(long)]
pub gas: Option<u64>,
#[clap(short, long)]
pub force: bool,
}
pub(crate) async fn bond_mixnode(client: Client, args: Args, denom: &str) {
info!("Starting mixnode bonding!");
// if we're trying to bond less than 1 token
if args.amount < 1_000_000 && !args.force {
warn!("You're trying to bond only {}{} which is less than 1 full token. Are you sure that's what you want? If so, run with `--force` or `-f` flag", args.amount, denom);
return;
}
let mixnode = mixnet_contract_common::MixNode {
host: args.host,
mix_port: args.mix_port.unwrap_or(DEFAULT_MIX_LISTENING_PORT),
verloc_port: args.verloc_port.unwrap_or(DEFAULT_VERLOC_LISTENING_PORT),
http_api_port: args
.http_api_port
.unwrap_or(DEFAULT_HTTP_API_LISTENING_PORT),
sphinx_key: args.sphinx_key,
identity_key: args.identity_key,
version: args.version,
profit_margin_percent: args.profit_margin_percent.unwrap_or(10),
};
let coin = Coin::new(args.amount, denom);
let res = client
.bond_mixnode(mixnode, args.signature, coin.into(), None)
.await
.expect("failed to bond mixnode!");
info!("Bonding result: {:?}", res)
}
@@ -1,26 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::Client;
use clap::Parser;
use log::info;
#[derive(Debug, Parser)]
pub(crate) struct Args {
#[clap(long)]
pub gas: Option<u64>,
#[clap(long)]
pub identity: String,
}
pub(crate) async fn claim_delegator_reward(client: Client, args: Args) {
info!("Claim delegator reward");
let res = client
.execute_claim_delegator_reward(args.identity, None)
.await
.expect("failed to claim delegator-reward");
info!("Claiming delegator reward: {:?}", res)
}
@@ -1,23 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::Client;
use clap::Parser;
use log::info;
#[derive(Debug, Parser)]
pub(crate) struct Args {
#[clap(long)]
pub gas: Option<u64>,
}
pub(crate) async fn claim_operator_reward(client: Client) {
info!("Claim operator reward");
let res = client
.execute_claim_operator_reward(None)
.await
.expect("failed to claim operator reward");
info!("Claiming operator reward: {:?}", res)
}
@@ -1,26 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::Client;
use clap::Parser;
use log::info;
#[derive(Debug, Parser)]
pub(crate) struct Args {
#[clap(long)]
pub gas: Option<u64>,
#[clap(long)]
pub identity: String,
}
pub(crate) async fn compound_delegator_reward(client: Client, args: Args) {
info!("compound delegator reward");
let res = client
.execute_compound_delegator_reward(args.identity, None)
.await
.expect("failed to claim delegator-reward");
info!("Claiming delegator reward: {:?}", res)
}
@@ -1,23 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::Client;
use clap::Parser;
use log::info;
#[derive(Debug, Parser)]
pub(crate) struct Args {
#[clap(long)]
pub gas: Option<u64>,
}
pub(crate) async fn compound_operator_reward(client: Client, _args: Args) {
info!("compounding operator reward");
let res = client
.execute_compound_operator_reward(None)
.await
.expect("failed to compound operator-reward");
info!("Claiming compound operator reward: {:?}", res)
}
@@ -1,24 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
use validator_client::nymd::wallet::DirectSecp256k1HdWallet;
#[derive(Debug, Parser)]
pub(crate) struct Args {
#[clap(long)]
// allowed values are 12, 18 or 24
pub word_count: Option<usize>,
}
pub(crate) fn create_account(args: Args, prefix: &str) {
let word_count = args.word_count.unwrap_or(24);
let mnemonic = bip39::Mnemonic::generate(word_count).expect("failed to generate mnemonic!");
let wallet =
DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic).expect("failed to build wallet!");
// Output address and mnemonics into separate lines for easier parsing
println!("{}", wallet.mnemonic());
println!("{}", wallet.try_derive_accounts().unwrap()[0].address());
}
@@ -1,17 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
#[derive(Debug, Parser)]
pub(crate) struct Args {
#[clap(short, long)]
pub key: String,
}
pub(crate) fn decode_mixnode_key(args: Args) {
let b64_decoded = base64::decode(args.key).expect("failed to decode base64 string");
let b58_encoded = bs58::encode(&b64_decoded).into_string();
println!("{}", b58_encoded)
}
@@ -1,32 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::Client;
use clap::Parser;
use log::info;
use mixnet_contract_common::Coin;
#[derive(Debug, Parser)]
pub(crate) struct Args {
#[clap(long)]
pub gas: Option<u64>,
#[clap(long)]
pub identity_key: String,
#[clap(long)]
pub amount: u128,
}
pub(crate) async fn delegate_to_mixnode(client: Client, args: Args, denom: &str) {
info!("Starting delegation to mixnode");
let coin = Coin::new(args.amount, denom);
let res = client
.delegate_to_mixnode(&*args.identity_key, coin.into(), None)
.await
.expect("failed to delegate to mixnode!");
info!("delegating to mixnode: {:?}", res);
}
@@ -1,74 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::Client;
use clap::Parser;
use log::info;
use mixnet_contract_common::InstantiateMsg;
use validator_client::nymd::cosmwasm_client::types::{ContractCodeId, InstantiateOptions};
#[derive(Debug, Parser)]
pub(crate) struct Args {
#[clap(long)]
pub code_id: ContractCodeId,
#[clap(long)]
pub memo: Option<String>,
#[clap(long)]
pub label: Option<String>,
#[clap(long)]
pub init_message: Option<String>,
#[clap(long)]
pub gas: Option<u64>,
}
pub(crate) async fn init(client: Client, args: Args, denom: &str) {
info!("Starting contract instantiation!");
let memo = args
.memo
.unwrap_or_else(|| "contract instantiation".to_owned());
let label = args
.label
.unwrap_or_else(|| "Nym mixnet smart contract".to_owned());
// by default we make ourselves an admin, let me know if you don't like that behaviour
let opts = Some(InstantiateOptions {
funds: vec![],
admin: Some(client.address().clone()),
});
// the EmptyMsg{} argument is equivalent to `--init-message='{}'`
let res = if let Some(raw_msg) = args.init_message {
let msg: serde_json::Value =
serde_json::from_str(&raw_msg).expect("failed to parse init message");
client
.instantiate(args.code_id, &msg, label, memo, opts, None)
.await
.expect("failed to instantiate the contract!")
} else {
let address = client.address().to_string();
client
.instantiate(
args.code_id,
&InstantiateMsg {
rewarding_validator_address: address,
mixnet_denom: denom.to_string(),
},
label,
memo,
opts,
None,
)
.await
.expect("failed to instantiate the contract!")
};
info!("Init result: {:?}", res);
println!("{}", res.contract_address)
}
@@ -1,47 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::Client;
use clap::Parser;
use log::info;
use validator_client::nymd::cosmwasm_client::types::{ContractCodeId, EmptyMsg};
#[derive(Debug, Parser)]
pub(crate) struct Args {
#[clap(long)]
pub code_id: ContractCodeId,
#[clap(long)]
pub memo: Option<String>,
#[clap(long)]
pub init_message: Option<String>,
#[clap(long)]
pub gas: Option<u64>,
}
pub(crate) async fn migrate(client: Client, args: Args) {
println!("Starting contract migration!");
let memo = args.memo.unwrap_or_else(|| "contract migration".to_owned());
let contract_address = client.mixnet_contract_address();
// the EmptyMsg{} argument is equivalent to `--init-message='{}'`
let res = if let Some(raw_msg) = args.init_message {
let msg: serde_json::Value =
serde_json::from_str(&raw_msg).expect("failed to parse init message");
client
.migrate(contract_address, args.code_id, &msg, memo, None)
.await
.expect("failed to instantiate the contract!")
} else {
client
.migrate(contract_address, args.code_id, &EmptyMsg {}, memo, None)
.await
.expect("failed to instantiate the contract!")
};
info!("Migrate result: {:?}", res);
}
@@ -1,32 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub(crate) mod bond_gateway;
pub(crate) mod bond_mixnode;
pub(crate) mod claim_delegator_reward;
pub(crate) mod claim_operator_reward;
pub(crate) mod compound_delegator_reward;
pub(crate) mod compound_operator_reward;
pub(crate) mod create_account;
pub(crate) mod decode_mixnode_key;
pub(crate) mod delegate_to_mixnode;
pub(crate) mod init_contract;
pub(crate) mod migrate_contract;
pub(crate) mod send;
pub(crate) mod unbond_gateway;
pub(crate) mod unbond_mixnode;
pub(crate) mod undelegate_from_mixnode;
pub(crate) mod update_profit_percent;
pub(crate) mod upload_contract;
pub(crate) mod vesting_bond_gateway;
pub(crate) mod vesting_bond_mixnode;
pub(crate) mod vesting_claim_delegator_reward;
pub(crate) mod vesting_claim_operator_reward;
pub(crate) mod vesting_compound_delegator_reward;
pub(crate) mod vesting_compound_operator_reward;
pub(crate) mod vesting_create_schedule;
pub(crate) mod vesting_delegate_to_mixnode;
pub(crate) mod vesting_unbond_gateway;
pub(crate) mod vesting_unbond_mixnode;
pub(crate) mod vesting_undelegate_from_mixnode;
pub(crate) mod vesting_update_profit_percent;
@@ -1,54 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::str::FromStr;
use crate::Client;
use clap::Parser;
use log::{info, warn};
use validator_client::nymd::{AccountId, CosmosCoin, Denom};
#[derive(Debug, Parser)]
pub(crate) struct Args {
#[clap(long)]
pub recipient: AccountId,
#[clap(
long,
help = "bonding amount in current DENOMINATION (so it would be 'unym', rather than 'nym')"
)]
pub amount: u64,
#[clap(long)]
pub memo: Option<String>,
#[clap(long)]
pub gas: Option<u64>,
#[clap(short, long)]
pub force: bool,
}
pub(crate) async fn send(client: Client, args: Args, denom: &str) {
info!("Starting token sending!");
let memo = args.memo.unwrap_or_else(|| "Sending tokens".to_owned());
// if we're trying to bond less than 1 token
if args.amount < 1_000_000 && !args.force {
warn!("You're trying to send only {}{} which is less than 1 full token. Are you sure that's what you want? If so, run with `--force` or `-f` flag", args.amount, denom);
return;
}
let coin = CosmosCoin {
denom: Denom::from_str(denom).unwrap(),
amount: args.amount.into(),
};
let res = client
.send(&args.recipient, vec![coin.into()], memo, None)
.await
.expect("failed to send tokens!");
info!("Sending result: {:?}", res)
}
@@ -1,23 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::Client;
use clap::Parser;
use log::info;
#[derive(Debug, Parser)]
pub(crate) struct Args {
#[clap(long)]
pub gas: Option<u64>,
}
pub(crate) async fn unbond_gateway(client: Client) {
info!("Starting gateway unbonding!");
let res = client
.unbond_gateway(None)
.await
.expect("failed to unbond gateway!");
info!("Unbonding result: {:?}", res)
}
@@ -1,23 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::Client;
use clap::Parser;
use log::info;
#[derive(Debug, Parser)]
pub(crate) struct Args {
#[clap(long)]
pub gas: Option<u64>,
}
pub(crate) async fn unbond_mixnode(client: Client) {
info!("Starting mixnode unbonding!");
let res = client
.unbond_mixnode(None)
.await
.expect("failed to unbond mixnode!");
info!("Unbonding result: {:?}", res)
}
@@ -1,26 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::Client;
use clap::Parser;
use log::info;
#[derive(Debug, Parser)]
pub(crate) struct Args {
#[clap(long)]
pub identity_key: String,
#[clap(long)]
pub gas: Option<u64>,
}
pub(crate) async fn undelegate_from_mixnode(client: Client, args: Args) {
info!("removing stake from mix-node");
let res = client
.remove_mixnode_delegation(&*args.identity_key, None)
.await
.expect("failed to remove stake from mixnode!");
info!("removing stake from mixnode: {:?}", res)
}
@@ -1,27 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::Client;
use clap::Parser;
use log::info;
#[derive(Debug, Parser)]
pub(crate) struct Args {
#[clap(long)]
pub profit_percent: u8,
#[clap(long)]
pub gas: Option<u64>,
}
pub(crate) async fn update_profit_percent(client: Client, args: Args) {
info!("Update mix node profit percent - get those rewards!");
//profit percent between 1-100
let res = client
.update_mixnode_config(args.profit_percent, None)
.await
.expect("updating mix-node profit percent");
info!("profit percentage updated: {:?}", res)
}
@@ -1,40 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::Client;
use clap::Parser;
use log::info;
use std::io::Read;
use std::path::PathBuf;
#[derive(Debug, Parser)]
pub(crate) struct Args {
#[clap(long)]
pub wasm_path: PathBuf,
#[clap(long)]
pub memo: Option<String>,
#[clap(long)]
pub gas: Option<u64>,
}
pub(crate) async fn upload(client: Client, args: Args) {
info!("Starting contract upload!");
let mut file = std::fs::File::open(args.wasm_path).expect("failed to open the wasm blob");
let mut data = Vec::new();
file.read_to_end(&mut data).unwrap();
let memo = args.memo.unwrap_or_else(|| "contract upload".to_owned());
let res = client
.upload(data, memo, None)
.await
.expect("failed to upload the contract!");
info!("Upload result: {:?}", res);
println!("{}", res.code_id)
}
@@ -1,79 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::Client;
use clap::Parser;
use log::{info, warn};
use mixnet_contract_common::{Coin, Gateway};
use network_defaults::{DEFAULT_CLIENT_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT};
use validator_client::nymd::VestingSigningClient;
#[derive(Debug, Parser)]
pub(crate) struct Args {
#[clap(long)]
pub host: String,
#[clap(long)]
pub signature: String,
#[clap(long)]
pub mix_port: Option<u16>,
#[clap(long)]
pub clients_port: Option<u16>,
#[clap(long)]
pub location: Option<String>,
#[clap(long)]
pub sphinx_key: String,
#[clap(long)]
pub identity_key: String,
#[clap(long)]
pub version: String,
#[clap(
long,
help = "bonding amount in current DENOMINATION (so it would be 'unym', rather than 'nym')"
)]
pub amount: u128,
#[clap(long)]
pub gas: Option<u64>,
#[clap(short, long)]
pub force: bool,
}
pub(crate) async fn vesting_bond_gateway(client: Client, args: Args, denom: &str) {
info!("Starting vesting gateway bonding!");
// if we're trying to bond less than 1 token
if args.amount < 1_000_000 && !args.force {
warn!("You're trying to bond only {}{} which is less than 1 full token. Are you sure that's what you want? If so, run with `--force` or `-f` flag", args.amount, denom);
return;
}
let gateway = Gateway {
host: args.host,
mix_port: args.mix_port.unwrap_or(DEFAULT_MIX_LISTENING_PORT),
clients_port: args.clients_port.unwrap_or(DEFAULT_CLIENT_LISTENING_PORT),
location: args
.location
.unwrap_or_else(|| "secret gateway location".to_owned()),
sphinx_key: args.sphinx_key,
identity_key: args.identity_key,
version: args.version,
};
let coin = Coin::new(args.amount, denom);
let res = client
.vesting_bond_gateway(gateway, &*args.signature, coin.into(), None)
.await
.expect("failed to bond gateway!");
info!("Vesting bonding gateway result: {:?}", res)
}
@@ -1,86 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::Client;
use clap::Parser;
use log::{info, warn};
use mixnet_contract_common::Coin;
use mixnet_contract_common::MixNode;
use network_defaults::{
DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT,
};
use validator_client::nymd::VestingSigningClient;
#[derive(Debug, Parser)]
pub(crate) struct Args {
#[clap(long)]
pub host: String,
#[clap(long)]
pub signature: String,
#[clap(long)]
pub mix_port: Option<u16>,
#[clap(long)]
pub verloc_port: Option<u16>,
#[clap(long)]
pub http_api_port: Option<u16>,
#[clap(long)]
pub sphinx_key: String,
#[clap(long)]
pub identity_key: String,
#[clap(long)]
pub version: String,
#[clap(long)]
pub profit_margin_percent: Option<u8>,
#[clap(
long,
help = "bonding amount in current DENOMINATION (so it would be 'unym', rather than 'nym')"
)]
pub amount: u128,
#[clap(long)]
pub gas: Option<u64>,
#[clap(short, long)]
pub force: bool,
}
pub(crate) async fn vesting_bond_mixnode(client: Client, args: Args, denom: &str) {
info!("Starting vesting mixnode bonding!");
// if we're trying to bond less than 1 token
if args.amount < 1_000_000 && !args.force {
warn!("You're trying to bond only {}{} which is less than 1 full token. Are you sure that's what you want? If so, run with `--force` or `-f` flag", args.amount, denom);
return;
}
let mixnode = MixNode {
host: args.host,
mix_port: args.mix_port.unwrap_or(DEFAULT_MIX_LISTENING_PORT),
verloc_port: args.verloc_port.unwrap_or(DEFAULT_VERLOC_LISTENING_PORT),
http_api_port: args
.http_api_port
.unwrap_or(DEFAULT_HTTP_API_LISTENING_PORT),
sphinx_key: args.sphinx_key,
identity_key: args.identity_key,
version: args.version,
profit_margin_percent: args.profit_margin_percent.unwrap_or(10),
};
let coin = Coin::new(args.amount, denom);
let res = client
.vesting_bond_mixnode(mixnode, &*args.signature, coin.into(), None)
.await
.expect("failed to bond vesting mixnode!");
info!("Bonding vesting result: {:?}", res)
}
@@ -1,26 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::Client;
use clap::Parser;
use log::info;
#[derive(Debug, Parser)]
pub(crate) struct Args {
#[clap(long)]
pub gas: Option<u64>,
#[clap(long)]
pub identity: String,
}
pub(crate) async fn vesting_claim_delegator_reward(client: Client, args: Args) {
info!("Claim vesting delegator reward");
let res = client
.execute_vesting_claim_delegator_reward(args.identity, None)
.await
.expect("failed to claim vesting delegator-reward");
info!("Claiming vesting delegator reward: {:?}", res)
}
@@ -1,23 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::Client;
use clap::Parser;
use log::info;
#[derive(Debug, Parser)]
pub(crate) struct Args {
#[clap(long)]
pub gas: Option<u64>,
}
pub(crate) async fn vesting_claim_operator_reward(client: Client) {
info!("Claim vesting operator reward");
let res = client
.execute_vesting_claim_operator_reward(None)
.await
.expect("failed to claim vesting operator reward");
info!("Claiming vesting operator reward: {:?}", res)
}
@@ -1,26 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::Client;
use clap::Parser;
use log::info;
#[derive(Debug, Parser)]
pub(crate) struct Args {
#[clap(long)]
pub gas: Option<u64>,
#[clap(long)]
pub identity: String,
}
pub(crate) async fn vesting_compound_delegator_reward(client: Client, args: Args) {
info!("Claim vesting delegator reward");
let res = client
.execute_vesting_compound_delegator_reward(args.identity, None)
.await
.expect("failed to claim vesting delegator-reward");
info!("Claiming vesting delegator reward: {:?}", res)
}
@@ -1,23 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::Client;
use clap::Parser;
use log::info;
#[derive(Debug, Parser)]
pub(crate) struct Args {
#[clap(long)]
pub gas: Option<u64>,
}
pub(crate) async fn vesting_compound_operator_reward(client: Client, _args: Args) {
info!("compounding vesting operator reward");
let res = client
.execute_vesting_compound_operator_reward(None)
.await
.expect("failed to compound operator-reward");
info!("Claiming compound operator reward: {:?}", res)
}
@@ -1,76 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::{AccountId, Client};
use clap::Parser;
use log::info;
use mixnet_contract_common::Coin;
use std::str::FromStr;
use validator_client::nymd::{CosmosCoin, Denom, VestingSigningClient};
use vesting_contract_common::messages::VestingSpecification;
#[derive(Debug, Parser)]
pub(crate) struct Args {
#[clap(long)]
pub periods_seconds: Option<u64>,
#[clap(long)]
pub number_of_periods: Option<u64>,
#[clap(long)]
pub start_time: Option<u64>,
#[clap(long)]
pub address: String,
#[clap(long)]
pub amount: u64,
#[clap(long)]
pub staking_address: Option<String>,
}
pub(crate) async fn vesting_create_schedule(client: Client, args: Args, denom: &str) {
info!("Creating vesting schedule!");
let vesting = VestingSpecification::new(
args.start_time,
args.periods_seconds,
args.number_of_periods,
);
let coin = Coin::new(args.amount.into(), denom);
let res = client
.create_periodic_vesting_account(
&*args.address,
args.staking_address,
Some(vesting),
coin.into(),
None,
)
.await
.expect("creating vesting schedule for the user!");
//send 1 coin
let coin_amount: u64 = 100_000;
let coin = CosmosCoin {
denom: Denom::from_str(denom).unwrap(),
amount: coin_amount.into(),
};
let send_coin_response = client
.send(
&AccountId::from_str(&*args.address).unwrap(),
vec![coin.into()],
"payment made :)",
None,
)
.await
.unwrap();
info!("Vesting result: {:?}", res);
info!("Coin send result: {:?}", send_coin_response);
}
@@ -1,33 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::Client;
use clap::Parser;
use log::info;
use mixnet_contract_common::Coin;
use validator_client::nymd::VestingSigningClient;
#[derive(Debug, Parser)]
pub(crate) struct Args {
#[clap(long)]
pub gas: Option<u64>,
#[clap(long)]
pub identity_key: String,
#[clap(long)]
pub amount: u128,
}
pub(crate) async fn vesting_delegate_to_mixnode(client: Client, args: Args, denom: &str) {
info!("Starting vesting delegation to mixnode");
let coin = Coin::new(args.amount, denom);
let res = client
.vesting_delegate_to_mixnode(&*args.identity_key, coin.into(), None)
.await
.expect("failed to delegate to mixnode!");
info!("vesting delegating to mixnode: {:?}", res);
}
@@ -1,23 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::Client;
use clap::Parser;
use log::info;
#[derive(Debug, Parser)]
pub(crate) struct Args {
#[clap(long)]
pub gas: Option<u64>,
}
pub(crate) async fn vesting_unbond_gateway(client: Client) {
info!("Starting vesting gateway unbonding!");
let res = client
.unbond_gateway(None)
.await
.expect("failed to unbond vesting gateway!");
info!("Unbonding vesting result: {:?}", res)
}
@@ -1,24 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::Client;
use clap::Parser;
use log::info;
use validator_client::nymd::VestingSigningClient;
#[derive(Debug, Parser)]
pub(crate) struct Args {
#[clap(long)]
pub gas: Option<u64>,
}
pub(crate) async fn vesting_unbond_mixnode(client: Client) {
info!("Starting vesting mixnode unbonding!");
let res = client
.vesting_unbond_mixnode(None)
.await
.expect("failed to unbond vesting mixnode!");
info!("Unbonding vesting result: {:?}", res)
}
@@ -1,27 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::Client;
use clap::Parser;
use log::info;
use validator_client::nymd::VestingSigningClient;
#[derive(Debug, Parser)]
pub(crate) struct Args {
#[clap(long)]
pub identity_key: String,
#[clap(long)]
pub gas: Option<u64>,
}
pub(crate) async fn vesting_undelegate_from_mixnode(client: Client, args: Args) {
info!("removing stake from vesting mix-node");
let res = client
.vesting_undelegate_from_mixnode(&*args.identity_key, None)
.await
.expect("failed to remove stake from vesting account on mixnode!");
info!("removing stake from vesting mixnode: {:?}", res)
}
@@ -1,28 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::Client;
use clap::Parser;
use log::info;
use validator_client::nymd::VestingSigningClient;
#[derive(Debug, Parser)]
pub(crate) struct Args {
#[clap(long)]
pub profit_percent: u8,
#[clap(long)]
pub gas: Option<u64>,
}
pub(crate) async fn vesting_update_profit_percent(client: Client, args: Args) {
info!("Update vesting mix node profit percent - get those rewards!");
//profit percent between 1-100
let res = client
.vesting_update_mixnode_config(args.profit_percent, None)
.await
.expect("updating vesting mix-node profit percent");
info!("profit percentage updated: {:?}", res)
}
-226
View File
@@ -1,226 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::bond_gateway::bond_gateway;
use crate::commands::bond_mixnode::bond_mixnode;
use crate::commands::claim_delegator_reward::claim_delegator_reward;
use crate::commands::claim_operator_reward::claim_operator_reward;
use crate::commands::compound_delegator_reward::compound_delegator_reward;
use crate::commands::compound_operator_reward::compound_operator_reward;
use crate::commands::create_account::create_account;
use crate::commands::decode_mixnode_key::decode_mixnode_key;
use crate::commands::delegate_to_mixnode::delegate_to_mixnode;
use crate::commands::init_contract::init;
use crate::commands::migrate_contract::migrate;
use crate::commands::send::send;
use crate::commands::unbond_gateway::unbond_gateway;
use crate::commands::unbond_mixnode::unbond_mixnode;
use crate::commands::undelegate_from_mixnode::undelegate_from_mixnode;
use crate::commands::update_profit_percent::update_profit_percent;
use crate::commands::upload_contract::upload;
use crate::commands::vesting_bond_gateway::vesting_bond_gateway;
use crate::commands::vesting_bond_mixnode::vesting_bond_mixnode;
use crate::commands::vesting_claim_delegator_reward::vesting_claim_delegator_reward;
use crate::commands::vesting_claim_operator_reward::vesting_claim_operator_reward;
use crate::commands::vesting_compound_delegator_reward::vesting_compound_delegator_reward;
use crate::commands::vesting_compound_operator_reward::vesting_compound_operator_reward;
use crate::commands::vesting_create_schedule::vesting_create_schedule;
use crate::commands::vesting_delegate_to_mixnode::vesting_delegate_to_mixnode;
use crate::commands::vesting_unbond_gateway::vesting_unbond_gateway;
use crate::commands::vesting_unbond_mixnode::vesting_unbond_mixnode;
use crate::commands::vesting_undelegate_from_mixnode::vesting_undelegate_from_mixnode;
use crate::commands::vesting_update_profit_percent::vesting_update_profit_percent;
use clap::Parser;
use log::{error, warn};
use network_defaults::{
setup_env,
var_names::{BECH32_PREFIX, MIX_DENOM},
NymNetworkDetails,
};
use validator_client::nymd::{self, AccountId, NymdClient, SigningNymdClient};
mod commands;
// we're always going to be using the signing client
pub(crate) type Client = validator_client::nymd::NymdClient<SigningNymdClient>;
#[derive(Debug, Parser)]
pub(crate) enum Command {
BondGateway(commands::bond_gateway::Args),
BondMixnode(commands::bond_mixnode::Args),
ClaimOperatorReward(commands::claim_operator_reward::Args),
ClaimDelegatorReward(commands::claim_delegator_reward::Args),
CompoundOperatorReward(commands::compound_operator_reward::Args),
CompoundDelegatorReward(commands::compound_delegator_reward::Args),
Init(commands::init_contract::Args),
Migrate(commands::migrate_contract::Args),
Upload(commands::upload_contract::Args),
UnbondMixnode(commands::unbond_mixnode::Args),
UnbondGateway(commands::unbond_gateway::Args),
DelegateToMixnode(commands::delegate_to_mixnode::Args),
UnDelegateFomMixnode(commands::undelegate_from_mixnode::Args),
Send(commands::send::Args),
CreateAccount(commands::create_account::Args),
UpdateProfitPercent(commands::update_profit_percent::Args),
DecodeMixnodeKey(commands::decode_mixnode_key::Args),
VestingUpdateProfitPercent(commands::vesting_update_profit_percent::Args),
VestingBondGateway(commands::vesting_bond_gateway::Args),
VestingBondMixnode(commands::vesting_bond_mixnode::Args),
VestingClaimDelegatorRewards(commands::vesting_claim_delegator_reward::Args),
VestingClaimOperatorRewards(commands::vesting_claim_operator_reward::Args),
VestingCompoundOperatorRewards(commands::vesting_compound_operator_reward::Args),
VestingCompoundDelegatorRewards(commands::vesting_compound_delegator_reward::Args),
VestingCreateSchedule(commands::vesting_create_schedule::Args),
VestingDelegateToMixnode(commands::vesting_delegate_to_mixnode::Args),
VestingUnbondGateway(commands::vesting_unbond_gateway::Args),
VestingUnbondMixnode(commands::vesting_unbond_mixnode::Args),
VestingUndelegateFromMixnode(commands::vesting_undelegate_from_mixnode::Args),
}
#[derive(Debug, Parser)]
#[clap(name = "validator-client-scripts")]
pub(crate) struct Args {
#[clap(long)]
pub(crate) config_env_file: Option<std::path::PathBuf>,
#[clap(long)]
pub(crate) nymd_url: Option<String>,
#[clap(long)]
pub(crate) mnemonic: Option<bip39::Mnemonic>,
// it can only be `None` in the case of contract upload or init
#[clap(long)]
pub(crate) mixnet_contract: Option<AccountId>,
// it can only be `None` in the case of contract upload or init
#[clap(long)]
pub(crate) vesting_contract: Option<AccountId>,
#[clap(subcommand)]
pub(crate) command: Command,
}
async fn execute(args: Args) {
let prefix = std::env::var(BECH32_PREFIX).expect("prefix not set");
let denom = std::env::var(MIX_DENOM).expect("denom not set");
// doesn't require the client
if let Command::CreateAccount(args) = args.command {
return create_account(args, &prefix);
}
if let Command::DecodeMixnodeKey(args) = args.command {
return decode_mixnode_key(args);
}
let client = create_client(&args);
// require the client
match args.command {
Command::BondGateway(args) => bond_gateway(client, args, &denom).await,
Command::BondMixnode(args) => bond_mixnode(client, args, &denom).await,
Command::ClaimOperatorReward(_args) => claim_operator_reward(client).await,
Command::ClaimDelegatorReward(args) => claim_delegator_reward(client, args).await,
Command::CompoundDelegatorReward(args) => compound_delegator_reward(client, args).await,
Command::CompoundOperatorReward(args) => compound_operator_reward(client, args).await,
Command::Init(args) => init(client, args, &denom).await,
Command::Migrate(args) => migrate(client, args).await,
Command::Upload(args) => upload(client, args).await,
Command::UnbondMixnode(_) => unbond_mixnode(client).await,
Command::UnbondGateway(_) => unbond_gateway(client).await,
Command::Send(args) => send(client, args, &denom).await,
Command::DelegateToMixnode(args) => delegate_to_mixnode(client, args, &denom).await,
Command::UnDelegateFomMixnode(args) => undelegate_from_mixnode(client, args).await,
Command::UpdateProfitPercent(args) => update_profit_percent(client, args).await,
Command::VestingUpdateProfitPercent(args) => {
vesting_update_profit_percent(client, args).await
}
Command::VestingBondGateway(args) => vesting_bond_gateway(client, args, &denom).await,
Command::VestingBondMixnode(args) => vesting_bond_mixnode(client, args, &denom).await,
Command::VestingClaimDelegatorRewards(args) => {
vesting_claim_delegator_reward(client, args).await
}
Command::VestingClaimOperatorRewards(_args) => vesting_claim_operator_reward(client).await,
Command::VestingCompoundDelegatorRewards(args) => {
vesting_compound_delegator_reward(client, args).await
}
Command::VestingCompoundOperatorRewards(args) => {
vesting_compound_operator_reward(client, args).await
}
Command::VestingCreateSchedule(args) => vesting_create_schedule(client, args, &denom).await,
Command::VestingDelegateToMixnode(args) => {
vesting_delegate_to_mixnode(client, args, &denom).await
}
Command::VestingUnbondGateway(_args) => vesting_unbond_gateway(client).await,
Command::VestingUnbondMixnode(_args) => vesting_unbond_mixnode(client).await,
Command::VestingUndelegateFromMixnode(args) => {
vesting_undelegate_from_mixnode(client, args).await
}
_ => unreachable!(),
}
}
fn setup_logging() {
let mut log_builder = pretty_env_logger::formatted_timed_builder();
if let Ok(s) = ::std::env::var("RUST_LOG") {
log_builder.parse_filters(&s);
} else {
// default to 'Info'
log_builder.filter(None, log::LevelFilter::Info);
}
log_builder
.filter_module("hyper", log::LevelFilter::Warn)
.filter_module("tokio_reactor", log::LevelFilter::Warn)
.filter_module("reqwest", log::LevelFilter::Warn)
.filter_module("mio", log::LevelFilter::Warn)
.filter_module("want", log::LevelFilter::Warn)
.filter_module("sled", log::LevelFilter::Warn)
.filter_module("tungstenite", log::LevelFilter::Warn)
.filter_module("tokio_tungstenite", log::LevelFilter::Warn)
.init();
}
fn create_client(args: &Args) -> Client {
let network_details = NymNetworkDetails::new_from_env();
let client_config = nymd::Config::try_from_nym_network_details(&network_details)
.expect("failed to construct valid validator client config with the provided network");
NymdClient::connect_with_mnemonic(
client_config,
args.nymd_url
.as_ref()
.expect("nymd url was not provided")
.as_str(),
args.mnemonic
.as_ref()
.expect("mnemonic was not provided")
.clone(),
None,
)
.expect("failed to create the client")
}
async fn wait_for_interrupt() {
if let Err(e) = tokio::signal::ctrl_c().await {
error!(
"There was an error while capturing SIGINT - {:?}. We will terminate regardless",
e
);
}
println!(
"Received SIGINT - the gateway will terminate now (threads are not yet nicely stopped, if you see stack traces that's alright)."
);
}
#[tokio::main]
async fn main() {
setup_logging();
let args = Args::parse();
setup_env(args.config_env_file.clone());
tokio::select! {
_ = wait_for_interrupt() => warn!("Received interrupt - the specified command might have not completed!"),
_ = execute(args) => (),
}
}
+1
View File
@@ -58,6 +58,7 @@ gateway-client = { path="../common/client-libs/gateway-client" }
mixnet-contract-common = { path= "../common/cosmwasm-smart-contracts/mixnet-contract" }
multisig-contract-common = { path = "../common/cosmwasm-smart-contracts/multisig-contract" }
nymsphinx = { path="../common/nymsphinx" }
task = { path = "../common/task" }
topology = { path="../common/topology" }
validator-api-requests = { path = "validator-api-requests" }
validator-client = { path="../common/client-libs/validator-client", features = ["nymd-client"] }
+17 -10
View File
@@ -14,6 +14,7 @@ use okapi::openapi3::OpenApi;
use rocket::Route;
use rocket_okapi::openapi_get_routes_spec;
use rocket_okapi::settings::OpenApiSettings;
use task::ShutdownListener;
use rocket::fairing::AdHoc;
use serde::Serialize;
@@ -252,20 +253,26 @@ impl<C> ValidatorCacheRefresher<C> {
Ok(())
}
pub(crate) async fn run(&self)
pub(crate) async fn run(&self, mut shutdown: ShutdownListener)
where
C: CosmWasmClient + Sync,
{
let mut interval = time::interval(self.caching_interval);
loop {
interval.tick().await;
if let Err(err) = self.refresh_cache().await {
error!("Failed to refresh validator cache - {}", err);
} else {
// relaxed memory ordering is fine here. worst case scenario network monitor
// will just have to wait for an additional backoff to see the change.
// And so this will not really incur any performance penalties by setting it every loop iteration
self.cache.initialised.store(true, Ordering::Relaxed)
while !shutdown.is_shutdown() {
tokio::select! {
_ = interval.tick() => {
if let Err(err) = self.refresh_cache().await {
error!("Failed to refresh validator cache - {}", err);
} else {
// relaxed memory ordering is fine here. worst case scenario network monitor
// will just have to wait for an additional backoff to see the change.
// And so this will not really incur any performance penalties by setting it every loop iteration
self.cache.initialised.store(true, Ordering::Relaxed)
}
}
_ = shutdown.recv() => {
trace!("UpdateHandler: Received shutdown");
}
}
}
}
+50 -12
View File
@@ -31,6 +31,7 @@ use std::str::FromStr;
use std::sync::Arc;
use std::time::Duration;
use std::{fs, process};
use task::ShutdownNotifier;
use tokio::sync::Notify;
// use validator_client::nymd::SigningNymdClient;
// use validator_client::ValidatorClientError;
@@ -226,14 +227,44 @@ fn parse_args<'a>() -> ArgMatches<'a> {
base_app.get_matches()
}
async fn wait_for_interrupt() {
if let Err(e) = tokio::signal::ctrl_c().await {
error!(
"There was an error while capturing SIGINT - {:?}. We will terminate regardless",
e
);
async fn wait_for_interrupt(mut shutdown: ShutdownNotifier) {
wait_for_signal().await;
log::info!("Sending shutdown");
shutdown.signal_shutdown().ok();
log::info!("Waiting for tasks to finish... (Press ctrl-c to force)");
shutdown.wait_for_shutdown().await;
log::info!("Stopping nym validator API");
}
#[cfg(unix)]
async fn wait_for_signal() {
use tokio::signal::unix::{signal, SignalKind};
let mut sigterm = signal(SignalKind::terminate()).expect("Failed to setup SIGTERM channel");
let mut sigquit = signal(SignalKind::quit()).expect("Failed to setup SIGQUIT channel");
tokio::select! {
_ = tokio::signal::ctrl_c() => {
log::info!("Received SIGINT");
},
_ = sigterm.recv() => {
log::info!("Received SIGTERM");
}
_ = sigquit.recv() => {
log::info!("Received SIGQUIT");
}
}
}
#[cfg(not(unix))]
async fn wait_for_signal() {
tokio::select! {
_ = tokio::signal::ctrl_c() => {
log::info!("Received SIGINT");
},
}
println!("Received SIGINT - the network monitor will terminate now");
}
fn setup_logging() {
@@ -547,6 +578,7 @@ async fn run_validator_api(matches: ArgMatches<'static>) -> Result<()> {
let signing_nymd_client = Client::new_signing(&config);
let liftoff_notify = Arc::new(Notify::new());
let shutdown = ShutdownNotifier::default();
// let's build our rocket!
let rocket = setup_rocket(
@@ -569,7 +601,8 @@ async fn run_validator_api(matches: ArgMatches<'static>) -> Result<()> {
// setup our daily uptime updater. Note that if network monitor is disabled, then we have
// no data for the updates and hence we don't need to start it up
let uptime_updater = HistoricalUptimeUpdater::new(storage.clone());
tokio::spawn(async move { uptime_updater.run().await });
let shutdown_listener = shutdown.subscribe();
tokio::spawn(async move { uptime_updater.run(shutdown_listener).await });
// spawn the cache refresher
let validator_cache_refresher = ValidatorCacheRefresher::new(
@@ -578,7 +611,8 @@ async fn run_validator_api(matches: ArgMatches<'static>) -> Result<()> {
validator_cache.clone(),
Some(storage.clone()),
);
tokio::spawn(async move { validator_cache_refresher.run().await });
let shutdown_listener = shutdown.subscribe();
tokio::spawn(async move { validator_cache_refresher.run(shutdown_listener).await });
// spawn rewarded set updater
let mut rewarded_set_updater =
@@ -593,11 +627,15 @@ async fn run_validator_api(matches: ArgMatches<'static>) -> Result<()> {
None,
);
let shutdown_listener = shutdown.subscribe();
// spawn our cacher
tokio::spawn(async move { validator_cache_refresher.run().await });
tokio::spawn(async move { validator_cache_refresher.run(shutdown_listener).await });
}
// launch the rocket!
// Rocket handles shutdown on it's own, but its shutdown handling should be incorporated
// with that of the rest of the tasks.
// Currently it's runtime is forcefully terminated once the validator-api exits.
let shutdown_handle = rocket.shutdown();
tokio::spawn(rocket.launch());
@@ -610,12 +648,12 @@ async fn run_validator_api(matches: ArgMatches<'static>) -> Result<()> {
// we're ready to go! spawn the network monitor!
let runnables = monitor_builder.build().await;
runnables.spawn_tasks();
runnables.spawn_tasks(&shutdown);
} else {
info!("Network monitoring is disabled.");
}
wait_for_interrupt().await;
wait_for_interrupt(shutdown).await;
shutdown_handle.notify();
Ok(())
+6 -3
View File
@@ -6,6 +6,7 @@ use crypto::asymmetric::{encryption, identity};
use futures::channel::mpsc;
use gateway_client::bandwidth::BandwidthController;
use std::sync::Arc;
use task::ShutdownNotifier;
use crate::config::Config;
use crate::contract_cache::ValidatorCache;
@@ -131,11 +132,13 @@ impl NetworkMonitorRunnables {
// TODO: note, that is not exactly doing what we want, because when
// `ReceivedProcessor` is constructed, it already spawns a future
// this needs to be refactored!
pub(crate) fn spawn_tasks(self) {
pub(crate) fn spawn_tasks(self, shutdown: &ShutdownNotifier) {
let mut packet_receiver = self.packet_receiver;
let mut monitor = self.monitor;
tokio::spawn(async move { packet_receiver.run().await });
tokio::spawn(async move { monitor.run().await });
let shutdown_listener = shutdown.subscribe();
tokio::spawn(async move { packet_receiver.run(shutdown_listener).await });
let shutdown_listener = shutdown.subscribe();
tokio::spawn(async move { monitor.run(shutdown_listener).await });
}
}
@@ -12,6 +12,7 @@ use crate::storage::ValidatorApiStorage;
use log::{debug, error, info};
use std::collections::{HashMap, HashSet};
use std::process;
use task::ShutdownListener;
use tokio::time::{sleep, Duration, Instant};
pub(crate) mod gateway_clients_cache;
@@ -296,7 +297,7 @@ impl Monitor {
self.test_nonce += 1;
}
pub(crate) async fn run(&mut self) {
pub(crate) async fn run(&mut self, mut shutdown: ShutdownListener) {
self.received_processor.start_receiving();
// wait for validator cache to be ready
@@ -308,9 +309,13 @@ impl Monitor {
.spawn_gateways_pinger(self.gateway_ping_interval);
let mut run_interval = tokio::time::interval(self.run_interval);
loop {
run_interval.tick().await;
self.test_run().await;
while !shutdown.is_shutdown() {
tokio::select! {
_ = run_interval.tick() => self.test_run().await,
_ = shutdown.recv() => {
trace!("UpdateHandler: Received shutdown");
}
}
}
}
}
@@ -7,6 +7,7 @@ use crypto::asymmetric::identity;
use futures::channel::mpsc;
use futures::StreamExt;
use gateway_client::{AcknowledgementReceiver, MixnetMessageReceiver};
use task::ShutdownListener;
pub(crate) type GatewayClientUpdateSender = mpsc::UnboundedSender<GatewayClientUpdate>;
pub(crate) type GatewayClientUpdateReceiver = mpsc::UnboundedReceiver<GatewayClientUpdate>;
@@ -55,8 +56,8 @@ impl PacketReceiver {
.expect("packet processor seems to have crashed!");
}
pub(crate) async fn run(&mut self) {
loop {
pub(crate) async fn run(&mut self, mut shutdown: ShutdownListener) {
while !shutdown.is_shutdown() {
tokio::select! {
// unwrap here is fine as it can only return a `None` if the PacketSender has died
// and if that was the case, then the entire monitor is already in an undefined state
@@ -67,6 +68,9 @@ impl PacketReceiver {
Some((_gateway_id, message)) = self.gateways_reader.stream_map().next() => {
self.process_gateway_messages(message)
}
_ = shutdown.recv() => {
trace!("UpdateHandler: Received shutdown");
}
}
}
}
@@ -7,6 +7,7 @@ use crate::node_status_api::models::{
use crate::node_status_api::ONE_DAY;
use crate::storage::ValidatorApiStorage;
use log::error;
use task::ShutdownListener;
use time::OffsetDateTime;
use tokio::time::sleep;
@@ -67,18 +68,23 @@ impl HistoricalUptimeUpdater {
Ok(())
}
pub(crate) async fn run(&self) {
loop {
// start any updates a day after starting the task so that we would have complete data
sleep(ONE_DAY).await;
if let Err(err) = self.update_uptimes().await {
// normally that would have been a warning rather than an error,
// however, in this case it implies some underlying issues with our database
// that might affect the entire program
error!(
"We failed to update daily uptimes of active nodes - {}",
err
)
pub(crate) async fn run(&self, mut shutdown: ShutdownListener) {
while !shutdown.is_shutdown() {
tokio::select! {
_ = sleep(ONE_DAY) => {
if let Err(err) = self.update_uptimes().await {
// normally that would have been a warning rather than an error,
// however, in this case it implies some underlying issues with our database
// that might affect the entire program
error!(
"We failed to update daily uptimes of active nodes - {}",
err
)
}
}
_ = shutdown.recv() => {
trace!("UpdateHandler: Received shutdown");
}
}
}
}