Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 56e09ee082 | |||
| 739b2f88f9 | |||
| c582d6dcba |
+1
-1
@@ -15,6 +15,6 @@ BANDWIDTH_CLAIM_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0
|
||||
COCONUT_BANDWIDTH_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0
|
||||
MULTISIG_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0
|
||||
REWARDING_VALIDATOR_ADDRESS=n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy
|
||||
STATISTICS_SERVICE_DOMAIN_ADDRESS="https://mainnet-stats.nymte.ch:8090"
|
||||
STATISTICS_SERVICE_DOMAIN_ADDRESS="https://mainnet-stats.nymte.ch"
|
||||
NYMD_VALIDATOR="https://rpc.nyx.nodes.guru/"
|
||||
API_VALIDATOR="https://validator.nymtech.net/api/"
|
||||
|
||||
@@ -160,7 +160,7 @@ mod qa {
|
||||
pub(crate) const STAKE_DENOM: DenomDetails = DenomDetails::new("unyx", "nyx", 6);
|
||||
|
||||
pub(crate) const MIXNET_CONTRACT_ADDRESS: &str =
|
||||
"n1suhgf5svhu4usrurvxzlgn54ksxmn8gljarjtxqnapv8kjnp4nrsd3qaep";
|
||||
"n1rjzps6qrmdqmf0xz4cn4x4rcmqeqzq6hnzqg4wcvd0r2lyasdq5sepn5s8";
|
||||
pub(crate) const VESTING_CONTRACT_ADDRESS: &str =
|
||||
"n1xr3rq8yvd7qplsw5yx90ftsr2zdhg4e9z60h5duusgxpv72hud3sjkxkav";
|
||||
pub(crate) const BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str =
|
||||
|
||||
@@ -13,7 +13,7 @@ export const ActionsMenu: React.FC<{ open: boolean; onOpen: () => void; onClose:
|
||||
return (
|
||||
<>
|
||||
<IconButton ref={anchorEl} onClick={onOpen}>
|
||||
<MoreVertSharp />
|
||||
<MoreVertSharp sx={{ color: (t) => t.palette.nym.nymWallet.text.main }} />
|
||||
</IconButton>
|
||||
<Menu anchorEl={anchorEl.current} open={open} onClose={onClose}>
|
||||
{children}
|
||||
|
||||
@@ -37,11 +37,11 @@ export const BondedGateway = ({
|
||||
const cells: Cell[] = [
|
||||
{
|
||||
cell: ip,
|
||||
id: 'stake-saturation-cell',
|
||||
id: 'ip-cell',
|
||||
},
|
||||
{
|
||||
cell: `${bond.amount} ${bond.denom}`,
|
||||
id: 'stake-cell',
|
||||
id: 'bond-cell',
|
||||
sx: { pl: 0 },
|
||||
},
|
||||
|
||||
|
||||
@@ -30,6 +30,11 @@ const headers: Header[] = [
|
||||
tooltipText:
|
||||
'The percentage of the node rewards that you as the node operator will take before the rest of the reward is shared between you and the delegators.',
|
||||
},
|
||||
{
|
||||
header: 'Operator cost',
|
||||
id: 'operator-cost',
|
||||
// tooltipText: 'TODO', // TODO
|
||||
},
|
||||
{
|
||||
header: 'Operator rewards',
|
||||
id: 'operator-rewards',
|
||||
@@ -55,8 +60,18 @@ export const BondedMixnode = ({
|
||||
network?: Network;
|
||||
onActionSelect: (action: TBondedMixnodeActions) => void;
|
||||
}) => {
|
||||
const { name, stake, bond, stakeSaturation, profitMargin, operatorRewards, delegators, status, identityKey } =
|
||||
mixnode;
|
||||
const {
|
||||
name,
|
||||
stake,
|
||||
bond,
|
||||
stakeSaturation,
|
||||
profitMargin,
|
||||
operatorRewards,
|
||||
operatorCost,
|
||||
delegators,
|
||||
status,
|
||||
identityKey,
|
||||
} = mixnode;
|
||||
const cells: Cell[] = [
|
||||
{
|
||||
cell: `${stake.amount} ${stake.denom}`,
|
||||
@@ -74,6 +89,10 @@ export const BondedMixnode = ({
|
||||
cell: `${profitMargin}%`,
|
||||
id: 'pm-cell',
|
||||
},
|
||||
{
|
||||
cell: operatorCost ? `${operatorCost} USD` : '-',
|
||||
id: 'operator-cost-cell',
|
||||
},
|
||||
{
|
||||
cell: operatorRewards ? `${operatorRewards.amount} ${operatorRewards.denom}` : '-',
|
||||
id: 'operator-rewards-cell',
|
||||
@@ -120,7 +139,7 @@ export const BondedMixnode = ({
|
||||
onClick={() => onActionSelect('nodeSettings')}
|
||||
startIcon={<NodeIcon />}
|
||||
>
|
||||
Settings
|
||||
Node Settings
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Typography } from '@mui/material';
|
||||
import { ActionsMenu, ActionsMenuItem } from 'src/components/ActionsMenu';
|
||||
import { Unbond as UnbondIcon } from '../../svg-icons';
|
||||
import { Unbond as UnbondIcon, Bond as BondIcon } from '../../svg-icons';
|
||||
|
||||
export type TBondedMixnodeActions = 'nodeSettings' | 'bondMore' | 'unbond' | 'redeem' | 'compound';
|
||||
export type TBondedMixnodeActions = 'nodeSettings' | 'bondMore' | 'unbond' | 'redeem';
|
||||
|
||||
export const BondedMixnodeActions = ({
|
||||
onActionSelect,
|
||||
@@ -24,25 +24,22 @@ export const BondedMixnodeActions = ({
|
||||
|
||||
return (
|
||||
<ActionsMenu open={isOpen} onOpen={handleOpen} onClose={handleClose}>
|
||||
<ActionsMenuItem
|
||||
title="Bond More"
|
||||
Icon={<BondIcon fontSize="inherit" />}
|
||||
onClick={() => handleActionClick('bondMore')}
|
||||
/>
|
||||
<ActionsMenuItem
|
||||
title="Redeem rewards"
|
||||
Icon={<Typography sx={{ pl: 1, fontWeight: 700 }}>R</Typography>}
|
||||
onClick={() => handleActionClick('redeem')}
|
||||
disabled={disabledRedeemAndCompound}
|
||||
/>
|
||||
<ActionsMenuItem
|
||||
title="Unbond"
|
||||
Icon={<UnbondIcon fontSize="inherit" />}
|
||||
onClick={() => handleActionClick('unbond')}
|
||||
/>
|
||||
<ActionsMenuItem
|
||||
title="Compound rewards"
|
||||
Icon={<Typography sx={{ pl: 1 }}>C</Typography>}
|
||||
description={disabledRedeemAndCompound ? 'No rewards to compound' : 'Add your rewards to you balance'}
|
||||
onClick={() => handleActionClick('compound')}
|
||||
disabled={disabledRedeemAndCompound}
|
||||
/>
|
||||
<ActionsMenuItem
|
||||
title="Redeem rewards"
|
||||
Icon={<Typography sx={{ pl: 1 }}>R</Typography>}
|
||||
description={disabledRedeemAndCompound ? 'No rewards to redeem' : 'Add your rewards to you balance'}
|
||||
onClick={() => handleActionClick('redeem')}
|
||||
disabled={disabledRedeemAndCompound}
|
||||
/>
|
||||
</ActionsMenu>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -13,6 +13,9 @@ import {
|
||||
bondGateway as bondGatewayRequest,
|
||||
bondMixNode as bondMixNodeRequest,
|
||||
claimOperatorReward,
|
||||
getGatewayBondDetails,
|
||||
getMixnodeBondDetails,
|
||||
getMixnodeRewardEstimation,
|
||||
unbondGateway as unbondGatewayRequest,
|
||||
unbondMixNode as unbondMixnodeRequest,
|
||||
vestingBondGateway,
|
||||
@@ -22,8 +25,6 @@ import {
|
||||
updateMixnodeCostParams as updateMixnodeCostParamsRequest,
|
||||
vestingUpdateMixnodeCostParams as updateMixnodeVestingCostParamsRequest,
|
||||
getNodeDescription as getNodeDescriptioRequest,
|
||||
getGatewayBondDetails,
|
||||
getMixnodeBondDetails,
|
||||
getMixnodeStatus,
|
||||
getPendingOperatorRewards,
|
||||
getMixnodeStakeSaturation,
|
||||
@@ -45,6 +46,8 @@ export type TBondedMixnode = {
|
||||
delegators: number;
|
||||
status: MixnodeStatus;
|
||||
proxy?: string;
|
||||
operatorCost?: number;
|
||||
host: string;
|
||||
};
|
||||
|
||||
export interface TBondedGateway {
|
||||
@@ -66,6 +69,7 @@ export type TBondingContext = {
|
||||
bondMixnode: (data: TBondMixNodeArgs, tokenPool: TokenPool) => Promise<TransactionExecuteResult | undefined>;
|
||||
bondGateway: (data: TBondGatewayArgs, tokenPool: TokenPool) => Promise<TransactionExecuteResult | undefined>;
|
||||
unbond: (fee?: FeeDetails) => Promise<TransactionExecuteResult | undefined>;
|
||||
bondMore: (signature: string, amount: DecCoin, fee?: FeeDetails) => Promise<TransactionExecuteResult | undefined>;
|
||||
redeemRewards: (fee?: FeeDetails) => Promise<TransactionExecuteResult | undefined>;
|
||||
updateMixnode: (pm: string, fee?: FeeDetails) => Promise<TransactionExecuteResult | undefined>;
|
||||
checkOwnership: () => Promise<void>;
|
||||
@@ -86,6 +90,9 @@ export const BondingContext = createContext<TBondingContext>({
|
||||
unbond: async () => {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
bondMore: async () => {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
redeemRewards: async () => {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
@@ -113,10 +120,12 @@ export const BondingContextProvider = ({ children }: { children?: React.ReactNod
|
||||
};
|
||||
|
||||
const getAdditionalMixnodeDetails = async (mixId: number) => {
|
||||
const additionalDetails: { status: MixnodeStatus; stakeSaturation: string } = {
|
||||
const additionalDetails: { status: MixnodeStatus; stakeSaturation: string; operatorCost?: number } = {
|
||||
status: 'not_found',
|
||||
stakeSaturation: '0',
|
||||
operatorCost: 0,
|
||||
};
|
||||
|
||||
try {
|
||||
const statusResponse = await getMixnodeStatus(mixId);
|
||||
additionalDetails.status = statusResponse.status;
|
||||
@@ -130,6 +139,12 @@ export const BondingContextProvider = ({ children }: { children?: React.ReactNod
|
||||
} catch (e) {
|
||||
Console.log(e);
|
||||
}
|
||||
try {
|
||||
const rewardEstimation = await getMixnodeRewardEstimation(mixId);
|
||||
additionalDetails.operatorCost = rewardEstimation.estimated_operator_cost;
|
||||
} catch (e) {
|
||||
Console.log(e);
|
||||
}
|
||||
return additionalDetails;
|
||||
};
|
||||
|
||||
@@ -155,7 +170,7 @@ export const BondingContextProvider = ({ children }: { children?: React.ReactNod
|
||||
Console.warn(`get_operator_rewards request failed: ${e}`);
|
||||
}
|
||||
if (data) {
|
||||
const { status, stakeSaturation } = await getAdditionalMixnodeDetails(data.bond_information.id);
|
||||
const { status, stakeSaturation, operatorCost } = await getAdditionalMixnodeDetails(data.bond_information.id);
|
||||
const nodeDescription = await getNodeDescription(
|
||||
data.bond_information.mix_node.host,
|
||||
data.bond_information.mix_node.http_api_port,
|
||||
@@ -175,6 +190,8 @@ export const BondingContextProvider = ({ children }: { children?: React.ReactNod
|
||||
operatorRewards,
|
||||
status,
|
||||
stakeSaturation,
|
||||
operatorCost,
|
||||
host: data.bond_information.mix_node.host,
|
||||
} as TBondedMixnode);
|
||||
}
|
||||
} catch (e: any) {
|
||||
|
||||
@@ -16,6 +16,8 @@ const bondedMixnodeMock: TBondedMixnode = {
|
||||
operatorRewards: { denom: 'nym', amount: '1234' },
|
||||
delegators: 5423,
|
||||
status: 'active',
|
||||
operatorCost: 2,
|
||||
host: '1.2.3.4',
|
||||
};
|
||||
|
||||
const bondedGatewayMock: TBondedGateway = {
|
||||
@@ -122,14 +124,6 @@ export const MockBondingContextProvider = ({
|
||||
return TxResultMock;
|
||||
};
|
||||
|
||||
const compoundRewards = async (): Promise<TransactionExecuteResult | undefined> => {
|
||||
setIsLoading(true);
|
||||
await mockSleep(SLEEP_MS);
|
||||
triggerStateUpdate();
|
||||
setIsLoading(false);
|
||||
return TxResultMock;
|
||||
};
|
||||
|
||||
const updateMixnode = async (): Promise<TransactionExecuteResult> => {
|
||||
setIsLoading(true);
|
||||
await mockSleep(SLEEP_MS);
|
||||
@@ -165,7 +159,6 @@ export const MockBondingContextProvider = ({
|
||||
unbond,
|
||||
refresh,
|
||||
redeemRewards,
|
||||
compoundRewards,
|
||||
fee,
|
||||
feeLoading,
|
||||
getFee,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import { FeeDetails } from '@nymproject/types';
|
||||
import { FeeDetails, DecCoin } from '@nymproject/types';
|
||||
import { TPoolOption } from 'src/components';
|
||||
import { Bond } from 'src/components/Bonding/Bond';
|
||||
import { BondedMixnode } from 'src/components/Bonding/BondedMixnode';
|
||||
@@ -17,17 +17,18 @@ import { BondedGateway } from 'src/components/Bonding/BondedGateway';
|
||||
import { RedeemRewardsModal } from 'src/components/Bonding/modals/RedeemRewardsModal';
|
||||
import { Box } from '@mui/material';
|
||||
import { BondingContextProvider, useBondingContext } from '../../context';
|
||||
import { BondMoreModal } from '../../components/Bonding/modals/BondMoreModal';
|
||||
|
||||
const Bonding = () => {
|
||||
const [showModal, setShowModal] = useState<
|
||||
'bond-mixnode' | 'bond-gateway' | 'bond-more' | 'unbond' | 'redeem' | 'compound' | 'node-settings'
|
||||
'bond-mixnode' | 'bond-gateway' | 'bond-more' | 'unbond' | 'redeem' | 'node-settings'
|
||||
>();
|
||||
const [confirmationDetails, setConfirmationDetails] = useState<ConfirmationDetailProps>();
|
||||
|
||||
const {
|
||||
network,
|
||||
clientDetails,
|
||||
userBalance: { originalVesting },
|
||||
userBalance: { originalVesting, balance, tokenAllocation },
|
||||
} = useContext(AppContext);
|
||||
|
||||
const {
|
||||
@@ -35,6 +36,7 @@ const Bonding = () => {
|
||||
bondMixnode,
|
||||
bondGateway,
|
||||
unbond,
|
||||
bondMore,
|
||||
updateMixnode,
|
||||
redeemRewards,
|
||||
isLoading,
|
||||
@@ -143,6 +145,23 @@ const Bonding = () => {
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const handleBondMore = async ({
|
||||
signature,
|
||||
additionalBond,
|
||||
}: {
|
||||
additionalBond: DecCoin;
|
||||
signature: string;
|
||||
tokenPool: TPoolOption;
|
||||
}) => {
|
||||
setShowModal(undefined);
|
||||
const tx = await bondMore(signature, additionalBond);
|
||||
setConfirmationDetails({
|
||||
status: 'success',
|
||||
title: 'Bond more successful',
|
||||
txUrl: `${urls(network).blockExplorer}/transaction/${tx?.transaction_hash}`,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Box sx={{ mt: 4 }}>
|
||||
{!bondedNode && <Bond disabled={isLoading} onBond={() => setShowModal('bond-mixnode')} />}
|
||||
@@ -180,6 +199,16 @@ const Bonding = () => {
|
||||
/>
|
||||
)}
|
||||
|
||||
{showModal === 'bond-more' && bondedNode && (
|
||||
<BondMoreModal
|
||||
currentBond={bondedNode.bond}
|
||||
userBalance={balance?.printable_balance}
|
||||
hasVestingTokens={Number(tokenAllocation?.vesting) > 0}
|
||||
onConfirm={handleBondMore}
|
||||
onClose={() => setShowModal(undefined)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{showModal === 'unbond' && bondedNode && (
|
||||
<UnbondModal
|
||||
node={bondedNode}
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
DecCoin,
|
||||
MixNodeDetails,
|
||||
GatewayBond,
|
||||
RewardEstimationResponse,
|
||||
WrappedDelegationEvent,
|
||||
} from '@nymproject/types';
|
||||
import { Interval, TNodeDescription } from 'src/types';
|
||||
@@ -22,8 +23,8 @@ export const getPendingOperatorRewards = async (address: string) =>
|
||||
export const getMixnodeStakeSaturation = async (mixId: number) =>
|
||||
invokeWrapper<StakeSaturationResponse>('mixnode_stake_saturation', { mixId });
|
||||
|
||||
// export const getMixnodeRewardEstimation = async (mixId: number) =>
|
||||
// invokeWrapper<RewardEstimationResponse>('mixnode_reward_estimation', { identity });
|
||||
export const getMixnodeRewardEstimation = async (mixId: number) =>
|
||||
invokeWrapper<RewardEstimationResponse>('mixnode_reward_estimation', { mixId });
|
||||
|
||||
export const getMixnodeStatus = async (mixId: number) =>
|
||||
invokeWrapper<MixnodeStatusResponse>('mixnode_status', { mixId });
|
||||
|
||||
Vendored
+1
@@ -40,6 +40,7 @@ declare module '@mui/material/styles' {
|
||||
grey: string;
|
||||
};
|
||||
linkHover: string;
|
||||
border: { menu: string };
|
||||
}
|
||||
|
||||
interface NymPaletteVariant {
|
||||
|
||||
@@ -32,6 +32,9 @@ const nymPalette: NymPalette = {
|
||||
grey: '#5B6174',
|
||||
},
|
||||
linkHover: '#AF4D36',
|
||||
border: {
|
||||
menu: '#E8E9EB',
|
||||
},
|
||||
};
|
||||
|
||||
const darkMode: NymPaletteVariant = {
|
||||
@@ -285,6 +288,15 @@ export const getDesignTokens = (mode: PaletteMode): ThemeOptions => {
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiMenu: {
|
||||
styleOverrides: {
|
||||
list: ({ _, theme }) => ({
|
||||
backgroundColor: theme.palette.mode === 'dark' ? darkMode.background.main : undefined,
|
||||
border: `1px solid ${theme.palette.nym.border.menu}`,
|
||||
borderRadius: '8px',
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
palette,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
export type RewardEstimationResponse = {
|
||||
estimated_total_node_reward: number;
|
||||
estimated_operator_reward: number;
|
||||
estimated_delegators_reward: number;
|
||||
estimated_node_profit: number;
|
||||
estimated_operator_cost: number;
|
||||
as_at: number;
|
||||
};
|
||||
@@ -40,6 +40,7 @@ export * from './PendingIntervalEventData';
|
||||
export * from './PendingUndelegate';
|
||||
export * from './Period';
|
||||
export * from './PledgeData';
|
||||
export * from './RewardEstimationResponse';
|
||||
export * from './RewardedSetNodeStatus';
|
||||
export * from './RewardingParams';
|
||||
export * from './RpcTransactionResponse';
|
||||
|
||||
Reference in New Issue
Block a user