Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| efdb8adab8 | |||
| e8632c3762 | |||
| fa354016e0 | |||
| 935ee765e9 | |||
| 4c8e59e6fc | |||
| 067f3e6f1a | |||
| fb0b928598 | |||
| e0e9d03b7f | |||
| 6f09d46dce | |||
| bdef48331b | |||
| 51a6936e51 | |||
| fd456d2952 | |||
| eee1abe593 | |||
| fffad43937 | |||
| 3a79f43a8d | |||
| 2e495f87ab | |||
| 57a9f18f5a | |||
| 0c6a0a9cae | |||
| c80d8d354a | |||
| 3f544dbc69 | |||
| d1e1f15db0 | |||
| 651c314182 | |||
| a57545521d | |||
| da60606921 | |||
| 14f9bf7234 | |||
| c1fa92869a | |||
| c8533e3ec8 | |||
| 06c4dd601d | |||
| 4ff80bbab2 | |||
| d7220b1fec | |||
| d92df9ada3 | |||
| 78ace473c7 | |||
| e877dfe224 | |||
| 30c488484c | |||
| 8cd155b2ac |
@@ -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
@@ -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
@@ -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 = [
|
||||
|
||||
@@ -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,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
@@ -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
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// master APIs
|
||||
export const API_BASE_URL = process.env.EXPLORER_API_URL;
|
||||
export const VALIDATOR_API_BASE_URL = process.env.VALIDATOR_API_URL;
|
||||
export const VALIDATOR_URL = process.env.VALIDATOR_URL;
|
||||
export const BIG_DIPPER = process.env.BIG_DIPPER_URL;
|
||||
|
||||
// specific API routes
|
||||
@@ -9,7 +10,7 @@ export const MIXNODE_PING = `${API_BASE_URL}/ping`;
|
||||
export const MIXNODES_API = `${API_BASE_URL}/mix-nodes`;
|
||||
export const MIXNODE_API = `${API_BASE_URL}/mix-node`;
|
||||
export const GATEWAYS_API = `${VALIDATOR_API_BASE_URL}/api/v1/gateways`;
|
||||
export const VALIDATORS_API = `${VALIDATOR_API_BASE_URL}/validators`;
|
||||
export const VALIDATORS_API = `${VALIDATOR_URL}/validators`;
|
||||
export const BLOCK_API = `${VALIDATOR_API_BASE_URL}/block`;
|
||||
export const COUNTRY_DATA_API = `${API_BASE_URL}/countries`;
|
||||
export const UPTIME_STORY_API = `${VALIDATOR_API_BASE_URL}/api/v1/status/mixnode`; // add ID then '/history' to this.
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
DialogContent,
|
||||
DialogActions,
|
||||
DialogTitle,
|
||||
IconButton,
|
||||
Slider,
|
||||
Typography,
|
||||
Box,
|
||||
@@ -25,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>
|
||||
|
||||
@@ -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),
|
||||
});
|
||||
|
||||
@@ -102,8 +102,8 @@ export const MainContextProvider: React.FC = ({ children }) => {
|
||||
m.mix_node.profit_margin_percent <= filters.profitMargin[1] &&
|
||||
m.stake_saturation >= filters.stakeSaturation[0] &&
|
||||
m.stake_saturation <= filters.stakeSaturation[1] &&
|
||||
+m.pledge_amount.amount + +m.total_delegation.amount >= filters.stake[0] &&
|
||||
+m.pledge_amount.amount + +m.total_delegation.amount <= filters.stake[1],
|
||||
m.avg_uptime >= filters.routingScore[0] &&
|
||||
m.avg_uptime <= filters.routingScore[1],
|
||||
);
|
||||
setMixnodes({ data: filtered, isLoading: false });
|
||||
};
|
||||
|
||||
@@ -4,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>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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}` }} />}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
|
||||
Vendored
+8
-5
@@ -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 { }
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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" }
|
||||
@@ -1,8 +0,0 @@
|
||||
install:
|
||||
cargo install --path .
|
||||
|
||||
clean:
|
||||
cargo clean
|
||||
|
||||
fmt:
|
||||
cargo fmt --all
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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) => (),
|
||||
}
|
||||
}
|
||||
@@ -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"] }
|
||||
|
||||
@@ -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
@@ -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,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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user