Compare commits

...

3 Commits

Author SHA1 Message Date
Bogdan-Ștefan Neacșu 56e09ee082 Remove port so we use proper 443 port 2022-10-04 17:40:58 +03:00
Pierre Dommerc 739b2f88f9 Wallet - update of bonding flow part 1 (#1528) 2022-10-04 13:46:51 +02:00
Pierre Dommerc c582d6dcba config(wallet): use new mixnet contract address for qa (#1656) 2022-10-03 13:38:56 +02:00
14 changed files with 120 additions and 42 deletions
+1 -1
View File
@@ -15,6 +15,6 @@ BANDWIDTH_CLAIM_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0
COCONUT_BANDWIDTH_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0
MULTISIG_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0
REWARDING_VALIDATOR_ADDRESS=n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy
STATISTICS_SERVICE_DOMAIN_ADDRESS="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/"
+1 -1
View File
@@ -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 =
+1 -1
View File
@@ -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>
);
};
+21 -4
View File
@@ -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) {
+2 -9
View File
@@ -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,
+32 -3
View File
@@ -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}
+3 -2
View File
@@ -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 });
+1
View File
@@ -40,6 +40,7 @@ declare module '@mui/material/styles' {
grey: string;
};
linkHover: string;
border: { menu: string };
}
interface NymPaletteVariant {
+12
View File
@@ -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';