Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 233b4590e7 | |||
| 3d973a8eb7 | |||
| 484f422eff | |||
| d90fe72785 | |||
| d15f9d9e07 | |||
| f184bdfb4b | |||
| 3c9988365a | |||
| 38b49940d7 | |||
| 41b5ffa872 | |||
| ca3f6bb511 | |||
| e380da4ba2 | |||
| 4d2974e383 | |||
| 88bde805f0 | |||
| b562137d23 | |||
| dc4a84cda9 | |||
| 87536b9f48 | |||
| 3a9d1b2503 | |||
| 7616104abb | |||
| 152b8dd320 | |||
| 5c6680209f | |||
| bb17c2ddfa |
@@ -234,6 +234,17 @@ impl<C> NymdClient<C> {
|
|||||||
self.client.get_balance(address, self.denom()?).await
|
self.client.get_balance(address, self.denom()?).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_denom_balance(
|
||||||
|
&self,
|
||||||
|
address: &AccountId,
|
||||||
|
denom: Denom,
|
||||||
|
) -> Result<Option<CosmosCoin>, NymdError>
|
||||||
|
where
|
||||||
|
C: CosmWasmClient + Sync,
|
||||||
|
{
|
||||||
|
self.client.get_balance(address, denom).await
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get_contract_settings(&self) -> Result<ContractStateParams, NymdError>
|
pub async fn get_contract_settings(&self) -> Result<ContractStateParams, NymdError>
|
||||||
where
|
where
|
||||||
C: CosmWasmClient + Sync,
|
C: CosmWasmClient + Sync,
|
||||||
|
|||||||
@@ -39,13 +39,10 @@ impl ValidatorDetails {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn default_validators() -> Vec<ValidatorDetails> {
|
pub fn default_validators() -> Vec<ValidatorDetails> {
|
||||||
vec![
|
vec![ValidatorDetails::new(
|
||||||
ValidatorDetails::new(
|
"https://nova-validator.nymtech.net",
|
||||||
"https://testnet-milhon-validator1.nymtech.net",
|
Some("https://nova-validator.nymtech.net/api"),
|
||||||
Some("https://testnet-milhon-validator1.nymtech.net/api"),
|
)]
|
||||||
),
|
|
||||||
ValidatorDetails::new("https://testnet-milhon-validator2.nymtech.net", None),
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn default_nymd_endpoints() -> Vec<Url> {
|
pub fn default_nymd_endpoints() -> Vec<Url> {
|
||||||
@@ -62,9 +59,9 @@ pub fn default_api_endpoints() -> Vec<Url> {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const DEFAULT_MIXNET_CONTRACT_ADDRESS: &str = "punk10pyejy66429refv3g35g2t7am0was7yalwrzen";
|
pub const DEFAULT_MIXNET_CONTRACT_ADDRESS: &str = "tnym14hj2tavq8fpesdwxxcu44rty3hh90vhu5ksfyn";
|
||||||
pub const DEFAULT_VESTING_CONTRACT_ADDRESS: &str = "";
|
pub const DEFAULT_VESTING_CONTRACT_ADDRESS: &str = "tnym17p9rzwnnfxcjp32un9ug7yhhzgtkhvl9epkgzu";
|
||||||
pub const REWARDING_VALIDATOR_ADDRESS: &str = "punk1v9qauwdq5terag6uvfsdytcs2d0sdmfdy7hgk3";
|
pub const REWARDING_VALIDATOR_ADDRESS: &str = "tnym1xwwst7fd30czjqlve0ww50t7x8mycs2sau5y85";
|
||||||
|
|
||||||
/// How much bandwidth (in bytes) one token can buy
|
/// How much bandwidth (in bytes) one token can buy
|
||||||
const BYTES_PER_TOKEN: u64 = 1024 * 1024 * 1024;
|
const BYTES_PER_TOKEN: u64 = 1024 * 1024 * 1024;
|
||||||
@@ -77,7 +74,7 @@ pub const BANDWIDTH_VALUE: u64 = TOKENS_TO_BURN * BYTES_PER_TOKEN;
|
|||||||
pub const ETH_CONTRACT_ADDRESS: [u8; 20] =
|
pub const ETH_CONTRACT_ADDRESS: [u8; 20] =
|
||||||
hex_literal::hex!("9fEE3e28c17dbB87310A51F13C4fbf4331A6f102");
|
hex_literal::hex!("9fEE3e28c17dbB87310A51F13C4fbf4331A6f102");
|
||||||
pub const ETH_MIN_BLOCK_DEPTH: usize = 7;
|
pub const ETH_MIN_BLOCK_DEPTH: usize = 7;
|
||||||
pub const COSMOS_CONTRACT_ADDRESS: &str = "punk1jld76tqw4wnpfenmay2xkv86nr3j0w426eka82";
|
pub const COSMOS_CONTRACT_ADDRESS: &str = "tnym1nc5tatafv6eyq7llkr2gv50ff9e22mnf3r3jqq";
|
||||||
// Name of the event triggered by the eth contract. If the event name is changed,
|
// Name of the event triggered by the eth contract. If the event name is changed,
|
||||||
// this would also need to be changed; It is currently tested against the json abi
|
// this would also need to be changed; It is currently tested against the json abi
|
||||||
pub const ETH_EVENT_NAME: &str = "Burned";
|
pub const ETH_EVENT_NAME: &str = "Burned";
|
||||||
@@ -85,8 +82,8 @@ pub const ETH_BURN_FUNCTION_NAME: &str = "burnTokenForAccessCode";
|
|||||||
|
|
||||||
/// Defaults Cosmos Hub/ATOM path
|
/// Defaults Cosmos Hub/ATOM path
|
||||||
pub const COSMOS_DERIVATION_PATH: &str = "m/44'/118'/0'/0/0";
|
pub const COSMOS_DERIVATION_PATH: &str = "m/44'/118'/0'/0/0";
|
||||||
pub const BECH32_PREFIX: &str = "punk";
|
pub const BECH32_PREFIX: &str = "tnym";
|
||||||
pub const DENOM: &str = "upunk";
|
pub const DENOM: &str = "utnym";
|
||||||
// as set by validators in their configs
|
// as set by validators in their configs
|
||||||
// (note that the 'amount' postfix is relevant here as the full gas price also includes denom)
|
// (note that the 'amount' postfix is relevant here as the full gas price also includes denom)
|
||||||
pub const GAS_PRICE_AMOUNT: f64 = 0.025;
|
pub const GAS_PRICE_AMOUNT: f64 = 0.025;
|
||||||
|
|||||||
@@ -47,12 +47,15 @@ pub(crate) fn ensure_no_existing_bond(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unreachable_code)]
|
||||||
|
#[allow(unused_variables)]
|
||||||
pub(crate) fn validate_node_identity_signature(
|
pub(crate) fn validate_node_identity_signature(
|
||||||
deps: Deps,
|
deps: Deps,
|
||||||
owner: &Addr,
|
owner: &Addr,
|
||||||
signature: String,
|
signature: String,
|
||||||
identity: IdentityKeyRef,
|
identity: IdentityKeyRef,
|
||||||
) -> Result<(), ContractError> {
|
) -> Result<(), ContractError> {
|
||||||
|
return Ok(());
|
||||||
let owner_bytes = owner.as_bytes();
|
let owner_bytes = owner.as_bytes();
|
||||||
|
|
||||||
let mut identity_bytes = [0u8; 32];
|
let mut identity_bytes = [0u8; 32];
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
"@babel/plugin-transform-async-to-generator": "^7.14.5",
|
"@babel/plugin-transform-async-to-generator": "^7.14.5",
|
||||||
"@babel/preset-env": "^7.15.0",
|
"@babel/preset-env": "^7.15.0",
|
||||||
"@babel/preset-react": "^7.14.5",
|
"@babel/preset-react": "^7.14.5",
|
||||||
|
"@svgr/webpack": "^6.1.1",
|
||||||
"@tauri-apps/api": "^1.0.0-beta.6",
|
"@tauri-apps/api": "^1.0.0-beta.6",
|
||||||
"@tauri-apps/cli": "^1.0.0-beta.9",
|
"@tauri-apps/cli": "^1.0.0-beta.9",
|
||||||
"@types/bs58": "^4.0.1",
|
"@types/bs58": "^4.0.1",
|
||||||
|
|||||||
@@ -1,46 +0,0 @@
|
|||||||
import React from 'react'
|
|
||||||
import {
|
|
||||||
Alert,
|
|
||||||
AlertTitle,
|
|
||||||
CircularProgress,
|
|
||||||
Grid,
|
|
||||||
Typography,
|
|
||||||
} from '@mui/material'
|
|
||||||
|
|
||||||
type ConfirmationProps = {
|
|
||||||
isLoading: boolean
|
|
||||||
progressMessage: string
|
|
||||||
SuccessMessage: React.ReactNode
|
|
||||||
failureMessage: string
|
|
||||||
error: Error | null
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Confirmation = ({
|
|
||||||
isLoading,
|
|
||||||
progressMessage,
|
|
||||||
SuccessMessage,
|
|
||||||
failureMessage,
|
|
||||||
error,
|
|
||||||
}: ConfirmationProps) => {
|
|
||||||
return isLoading ? (
|
|
||||||
<>
|
|
||||||
<Typography variant="h6" gutterBottom>
|
|
||||||
{progressMessage}
|
|
||||||
</Typography>
|
|
||||||
<Grid item xs={12} sm={6}>
|
|
||||||
<CircularProgress />
|
|
||||||
</Grid>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
{error === null ? (
|
|
||||||
SuccessMessage
|
|
||||||
) : (
|
|
||||||
<Alert severity="error" data-testid="error-message">
|
|
||||||
<AlertTitle>{error.name}</AlertTitle>
|
|
||||||
<strong>{failureMessage}</strong> - {error.message}
|
|
||||||
</Alert>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,18 +1,9 @@
|
|||||||
import React, { useContext, useEffect } from 'react'
|
import React, { useContext, useEffect } from 'react'
|
||||||
import { Link, useLocation } from 'react-router-dom'
|
import { Link, useLocation } from 'react-router-dom'
|
||||||
import { List, ListItem, ListItemIcon, ListItemText } from '@mui/material'
|
import { List, ListItem, ListItemIcon, ListItemText } from '@mui/material'
|
||||||
import {
|
import { AccountBalanceWalletOutlined, ArrowBack, ArrowForward, Description, Settings } from '@mui/icons-material'
|
||||||
AccountBalanceWalletOutlined,
|
|
||||||
ArrowBack,
|
|
||||||
ArrowForward,
|
|
||||||
AttachMoney,
|
|
||||||
CancelOutlined,
|
|
||||||
HowToVoteOutlined,
|
|
||||||
MoneyOff,
|
|
||||||
Description,
|
|
||||||
Settings,
|
|
||||||
} from '@mui/icons-material'
|
|
||||||
import { ADMIN_ADDRESS, ClientContext } from '../context/main'
|
import { ADMIN_ADDRESS, ClientContext } from '../context/main'
|
||||||
|
import { Bond, Delegate, Unbond, Undelegate } from '../svg-icons'
|
||||||
|
|
||||||
let routesSchema = [
|
let routesSchema = [
|
||||||
{
|
{
|
||||||
@@ -33,22 +24,22 @@ let routesSchema = [
|
|||||||
{
|
{
|
||||||
label: 'Bond',
|
label: 'Bond',
|
||||||
route: '/bond',
|
route: '/bond',
|
||||||
Icon: AttachMoney,
|
Icon: Bond,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Unbond',
|
label: 'Unbond',
|
||||||
route: '/unbond',
|
route: '/unbond',
|
||||||
Icon: MoneyOff,
|
Icon: Unbond,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Delegate',
|
label: 'Delegate',
|
||||||
route: '/delegate',
|
route: '/delegate',
|
||||||
Icon: HowToVoteOutlined,
|
Icon: Delegate,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Undelegate',
|
label: 'Undelegate',
|
||||||
route: '/undelegate',
|
route: '/undelegate',
|
||||||
Icon: CancelOutlined,
|
Icon: Undelegate,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -80,16 +71,14 @@ export const Nav = () => {
|
|||||||
<ListItemIcon
|
<ListItemIcon
|
||||||
sx={{
|
sx={{
|
||||||
minWidth: 30,
|
minWidth: 30,
|
||||||
color:
|
color: location.pathname === route ? 'primary.main' : 'common.white',
|
||||||
location.pathname === route ? 'primary.main' : 'common.white',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Icon sx={{ fontSize: 20 }} />
|
<Icon sx={{ fontSize: 20 }} />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<ListItemText
|
<ListItemText
|
||||||
sx={{
|
sx={{
|
||||||
color:
|
color: location.pathname === route ? 'primary.main' : 'common.white',
|
||||||
location.pathname === route ? 'primary.main' : 'common.white',
|
|
||||||
}}
|
}}
|
||||||
primary={label}
|
primary={label}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,11 +1,5 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {
|
import { FormControl, FormControlLabel, FormLabel, Radio, RadioGroup } from '@mui/material'
|
||||||
FormControl,
|
|
||||||
FormControlLabel,
|
|
||||||
FormLabel,
|
|
||||||
Radio,
|
|
||||||
RadioGroup,
|
|
||||||
} from '@mui/material'
|
|
||||||
import { EnumNodeType } from '../types/global'
|
import { EnumNodeType } from '../types/global'
|
||||||
|
|
||||||
export const NodeTypeSelector = ({
|
export const NodeTypeSelector = ({
|
||||||
@@ -17,8 +11,7 @@ export const NodeTypeSelector = ({
|
|||||||
nodeType: EnumNodeType
|
nodeType: EnumNodeType
|
||||||
setNodeType: (nodeType: EnumNodeType) => void
|
setNodeType: (nodeType: EnumNodeType) => void
|
||||||
}) => {
|
}) => {
|
||||||
const handleNodeTypeChange = (e: React.ChangeEvent<HTMLInputElement>) =>
|
const handleNodeTypeChange = (e: React.ChangeEvent<HTMLInputElement>) => setNodeType(e.target.value as EnumNodeType)
|
||||||
setNodeType(e.target.value as EnumNodeType)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormControl component="fieldset">
|
<FormControl component="fieldset">
|
||||||
@@ -32,14 +25,14 @@ export const NodeTypeSelector = ({
|
|||||||
>
|
>
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
value={EnumNodeType.mixnode}
|
value={EnumNodeType.mixnode}
|
||||||
control={<Radio />}
|
control={<Radio color="default" />}
|
||||||
label="Mixnode"
|
label="Mixnode"
|
||||||
data-testid="mix-node"
|
data-testid="mix-node"
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
value={EnumNodeType.gateway}
|
value={EnumNodeType.gateway}
|
||||||
control={<Radio />}
|
control={<Radio color="default" />}
|
||||||
data-testid="gate-way"
|
data-testid="gate-way"
|
||||||
label="Gateway"
|
label="Gateway"
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Card, CardContent, CardHeader } from '@mui/material'
|
import { Card, CardContent, CardHeader } from '@mui/material'
|
||||||
import { styled } from '@mui/styles'
|
import { styled } from '@mui/material/styles'
|
||||||
|
|
||||||
export const NymCard: React.FC<{
|
export const NymCard: React.FC<{
|
||||||
title: string
|
title: string
|
||||||
@@ -19,22 +19,20 @@ export const NymCard: React.FC<{
|
|||||||
action={Action}
|
action={Action}
|
||||||
sx={{
|
sx={{
|
||||||
color: 'nym.background.dark',
|
color: 'nym.background.dark',
|
||||||
padding: 2.5,
|
py: 2.5,
|
||||||
borderBottom: (theme) => `1px solid ${theme.palette.grey[100]}`,
|
px: 4,
|
||||||
|
borderBottom: (theme) => `1px solid ${theme.palette.grey[200]}`,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{noPadding ? (
|
{noPadding ? <CardContentNoPadding>{children}</CardContentNoPadding> : <CardContent>{children}</CardContent>}
|
||||||
<CardContentNoPadding>{children}</CardContentNoPadding>
|
|
||||||
) : (
|
|
||||||
<CardContent>{children}</CardContent>
|
|
||||||
)}
|
|
||||||
</Card>
|
</Card>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const CardContentNoPadding = styled(CardContent)({
|
const CardContentNoPadding = styled(CardContent)(({ theme }) => ({
|
||||||
|
background: theme.palette.grey[50],
|
||||||
padding: 0,
|
padding: 0,
|
||||||
'&:last-child': {
|
'&:last-child': {
|
||||||
paddingBottom: 0,
|
paddingBottom: 0,
|
||||||
},
|
},
|
||||||
})
|
}))
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { Stack, Typography } from '@mui/material'
|
||||||
|
|
||||||
|
export const SuccessReponse: React.FC<{
|
||||||
|
title: string
|
||||||
|
subtitle: string | React.ReactNode
|
||||||
|
caption: string | React.ReactNode
|
||||||
|
}> = ({ title, subtitle, caption }) => {
|
||||||
|
return (
|
||||||
|
<Stack spacing={3} alignItems="center" sx={{ mb: 5 }}>
|
||||||
|
<Typography variant="h5" fontWeight="600" data-testid="transaction-complete" color="success.main">
|
||||||
|
{title}
|
||||||
|
</Typography>
|
||||||
|
<Typography fontWeight="600">{subtitle}</Typography>
|
||||||
|
<Typography>{caption}</Typography>
|
||||||
|
</Stack>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { Card, Grid, Typography } from '@mui/material'
|
||||||
|
|
||||||
|
export type TTransactionDetails = Array<{ primary: string; secondary: string }>
|
||||||
|
|
||||||
|
export const TransactionDetails: React.FC<{ details: TTransactionDetails }> = ({ details }) => {
|
||||||
|
return (
|
||||||
|
<Card variant="outlined" sx={{ width: '100%', p: 2 }}>
|
||||||
|
{details.map(({ primary, secondary }, i) => {
|
||||||
|
return (
|
||||||
|
<Grid container sx={{ mt: i !== 0 ? 1 : 0 }}>
|
||||||
|
<Grid item sm={4} md={3} lg={2}>
|
||||||
|
<Typography sx={{ color: (theme) => theme.palette.grey[600] }}>{primary}</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item>
|
||||||
|
<Typography data-testid="to-address">{secondary}</Typography>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
export * from './AdminForm'
|
export * from './AdminForm'
|
||||||
export * from './Error'
|
export * from './Error'
|
||||||
export * from './Confirmation'
|
|
||||||
export * from './CopyToClipboard'
|
export * from './CopyToClipboard'
|
||||||
export * from './NymCard'
|
export * from './NymCard'
|
||||||
export * from './Nav'
|
export * from './Nav'
|
||||||
export * from './NodeTypeSelector'
|
export * from './NodeTypeSelector'
|
||||||
export * from './RequestStatus'
|
export * from './RequestStatus'
|
||||||
export * from './NoClientError'
|
export * from './NoClientError'
|
||||||
|
export * from './SuccessResponse'
|
||||||
|
export * from './TransactionDetails'
|
||||||
|
|||||||
@@ -20,11 +20,7 @@ type TClientContext = {
|
|||||||
|
|
||||||
export const ClientContext = createContext({} as TClientContext)
|
export const ClientContext = createContext({} as TClientContext)
|
||||||
|
|
||||||
export const ClientContextProvider = ({
|
export const ClientContextProvider = ({ children }: { children: React.ReactNode }) => {
|
||||||
children,
|
|
||||||
}: {
|
|
||||||
children: React.ReactNode
|
|
||||||
}) => {
|
|
||||||
const [clientDetails, setClientDetails] = useState<TClientDetails>()
|
const [clientDetails, setClientDetails] = useState<TClientDetails>()
|
||||||
const [showAdmin, setShowAdmin] = useState(false)
|
const [showAdmin, setShowAdmin] = useState(false)
|
||||||
const [mode, setMode] = useState<'light' | 'dark'>('light')
|
const [mode, setMode] = useState<'light' | 'dark'>('light')
|
||||||
@@ -41,8 +37,7 @@ export const ClientContextProvider = ({
|
|||||||
}
|
}
|
||||||
}, [clientDetails, userBalance.fetchBalance])
|
}, [clientDetails, userBalance.fetchBalance])
|
||||||
|
|
||||||
const logIn = async (clientDetails: TSignInWithMnemonic) =>
|
const logIn = async (clientDetails: TSignInWithMnemonic) => setClientDetails(clientDetails)
|
||||||
setClientDetails(clientDetails)
|
|
||||||
|
|
||||||
const logOut = () => {
|
const logOut = () => {
|
||||||
setClientDetails(undefined)
|
setClientDetails(undefined)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useState } from 'react'
|
import { useCallback, useState } from 'react'
|
||||||
import { checkGatewayOwnership, checkMixnodeOwnership } from '../requests'
|
import { checkGatewayOwnership, checkMixnodeOwnership } from '../requests'
|
||||||
import { EnumNodeType, TNodeOwnership } from '../types'
|
import { EnumNodeType, TNodeOwnership } from '../types'
|
||||||
|
|
||||||
@@ -10,7 +10,7 @@ export const useCheckOwnership = () => {
|
|||||||
const [isLoading, setIsLoading] = useState(false)
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
const [error, setError] = useState<string>()
|
const [error, setError] = useState<string>()
|
||||||
|
|
||||||
const checkOwnership = async () => {
|
const checkOwnership = useCallback(async () => {
|
||||||
const status = {} as TNodeOwnership
|
const status = {} as TNodeOwnership
|
||||||
|
|
||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
@@ -33,7 +33,7 @@ export const useCheckOwnership = () => {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
setError(e as string)
|
setError(e as string)
|
||||||
}
|
}
|
||||||
}
|
}, [])
|
||||||
|
|
||||||
return { isLoading, error, ownership, checkOwnership }
|
return { isLoading, error, ownership, checkOwnership }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M4 12V15H6V12H4ZM16 7L14.59 5.59L13 7.17V2H11V7.19L9.39 5.61L8 7L12 11L16 7ZM4 17H20V15H4V17Z" fill="black"/>
|
||||||
|
<path d="M20 21C20 21.5523 19.5523 22 19 22H5C4.44772 22 4 21.5523 4 21V20H20V21Z" fill="black"/>
|
||||||
|
<rect x="18" y="12" width="2" height="3" fill="black"/>
|
||||||
|
<rect x="18" y="17" width="2" height="3" fill="black"/>
|
||||||
|
<rect x="4" y="17" width="2" height="3" fill="black"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 487 B |
@@ -1,5 +1,3 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- Generator: Adobe Illustrator 25.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
|
||||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
viewBox="0 0 5389.9 5389.9" style="enable-background:new 0 0 5389.9 5389.9;" xml:space="preserve">
|
viewBox="0 0 5389.9 5389.9" style="enable-background:new 0 0 5389.9 5389.9;" xml:space="preserve">
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.2 KiB |
@@ -20,11 +20,12 @@ export const ApplicationLayout: React.FC = ({ children }) => {
|
|||||||
sx={{
|
sx={{
|
||||||
background: '#121726',
|
background: '#121726',
|
||||||
overflow: 'auto',
|
overflow: 'auto',
|
||||||
p: [4, 5],
|
py: 4,
|
||||||
|
px: 5,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{ mb: 3 }}>
|
<Box sx={{ mb: 3 }}>
|
||||||
<img src={Logo} style={{ width: 45 }} />
|
<Logo width={45} />
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Nav />
|
<Nav />
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import React, { useContext } from 'react'
|
import React, { useContext, useEffect } from 'react'
|
||||||
import { Alert, Button, Grid, Link } from '@mui/material'
|
import { Alert, Button, Grid, Link, Typography } from '@mui/material'
|
||||||
import { Box } from '@mui/system'
|
|
||||||
import { OpenInNew } from '@mui/icons-material'
|
import { OpenInNew } from '@mui/icons-material'
|
||||||
import { NymCard } from '../components'
|
import { NymCard } from '../components'
|
||||||
import { Layout } from '../layouts'
|
import { Layout } from '../layouts'
|
||||||
@@ -10,6 +9,10 @@ import { ClientContext, urls } from '../context/main'
|
|||||||
export const Balance = () => {
|
export const Balance = () => {
|
||||||
const { userBalance, clientDetails } = useContext(ClientContext)
|
const { userBalance, clientDetails } = useContext(ClientContext)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
userBalance.fetchBalance()
|
||||||
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
<NymCard title="Balance" data-testid="check-balance">
|
<NymCard title="Balance" data-testid="check-balance">
|
||||||
@@ -21,10 +24,14 @@ export const Balance = () => {
|
|||||||
</Alert>
|
</Alert>
|
||||||
)}
|
)}
|
||||||
{!userBalance.error && (
|
{!userBalance.error && (
|
||||||
<Box data-testid="refresh-success" sx={{ p: 2 }}>
|
<Typography
|
||||||
{'The current balance is ' +
|
data-testid="refresh-success"
|
||||||
userBalance.balance?.printable_balance}
|
sx={{ p: 2, color: 'nym.background.dark' }}
|
||||||
</Box>
|
variant="h5"
|
||||||
|
fontWeight="700"
|
||||||
|
>
|
||||||
|
{userBalance.balance?.printable_balance}
|
||||||
|
</Typography>
|
||||||
)}
|
)}
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item>
|
<Grid item>
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
Grid,
|
Grid,
|
||||||
InputAdornment,
|
InputAdornment,
|
||||||
TextField,
|
TextField,
|
||||||
|
Typography,
|
||||||
} from '@mui/material'
|
} from '@mui/material'
|
||||||
import { yupResolver } from '@hookform/resolvers/yup'
|
import { yupResolver } from '@hookform/resolvers/yup'
|
||||||
import { useForm } from 'react-hook-form'
|
import { useForm } from 'react-hook-form'
|
||||||
@@ -81,7 +82,7 @@ export const BondForm = ({
|
|||||||
disabled: boolean
|
disabled: boolean
|
||||||
fees?: { [EnumNodeType.mixnode]: Coin; [EnumNodeType.gateway]?: Coin }
|
fees?: { [EnumNodeType.mixnode]: Coin; [EnumNodeType.gateway]?: Coin }
|
||||||
onError: (message?: string) => void
|
onError: (message?: string) => void
|
||||||
onSuccess: (message?: string) => void
|
onSuccess: (details: { address: string; amount: string }) => void
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
@@ -98,10 +99,7 @@ export const BondForm = ({
|
|||||||
const { userBalance } = useContext(ClientContext)
|
const { userBalance } = useContext(ClientContext)
|
||||||
|
|
||||||
const watchNodeType = watch('nodeType', defaultValues.nodeType)
|
const watchNodeType = watch('nodeType', defaultValues.nodeType)
|
||||||
const watchAdvancedOptions = watch(
|
const watchAdvancedOptions = watch('withAdvancedOptions', defaultValues.withAdvancedOptions)
|
||||||
'withAdvancedOptions',
|
|
||||||
defaultValues.withAdvancedOptions,
|
|
||||||
)
|
|
||||||
|
|
||||||
const onSubmit = async (data: TBondFormFields) => {
|
const onSubmit = async (data: TBondFormFields) => {
|
||||||
const hasEnoughFunds = await checkHasEnoughFunds(data.amount)
|
const hasEnoughFunds = await checkHasEnoughFunds(data.amount)
|
||||||
@@ -115,7 +113,7 @@ export const BondForm = ({
|
|||||||
await bond({ type: data.nodeType, data: formattedData, amount })
|
await bond({ type: data.nodeType, data: formattedData, amount })
|
||||||
.then(() => {
|
.then(() => {
|
||||||
userBalance.fetchBalance()
|
userBalance.fetchBalance()
|
||||||
onSuccess(`Successfully bonded to ${data.identityKey}`)
|
onSuccess({ address: data.identityKey, amount: data.amount })
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
onError(e)
|
onError(e)
|
||||||
@@ -132,23 +130,11 @@ export const BondForm = ({
|
|||||||
nodeType={watchNodeType}
|
nodeType={watchNodeType}
|
||||||
setNodeType={(nodeType) => {
|
setNodeType={(nodeType) => {
|
||||||
setValue('nodeType', nodeType)
|
setValue('nodeType', nodeType)
|
||||||
if (nodeType === EnumNodeType.mixnode)
|
if (nodeType === EnumNodeType.mixnode) setValue('location', undefined)
|
||||||
setValue('location', undefined)
|
|
||||||
}}
|
}}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
{fees && (
|
|
||||||
<Grid item>
|
|
||||||
<Alert severity="info" data-testid="fee-amount">
|
|
||||||
{`A fee of ${
|
|
||||||
watchNodeType === EnumNodeType.mixnode
|
|
||||||
? fees.mixnode.amount
|
|
||||||
: fees?.gateway?.amount
|
|
||||||
} PUNK will apply to this transaction`}
|
|
||||||
</Alert>
|
|
||||||
</Grid>
|
|
||||||
)}
|
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<TextField
|
<TextField
|
||||||
@@ -190,9 +176,7 @@ export const BondForm = ({
|
|||||||
error={!!errors.amount}
|
error={!!errors.amount}
|
||||||
helperText={errors.amount?.message}
|
helperText={errors.amount?.message}
|
||||||
InputProps={{
|
InputProps={{
|
||||||
endAdornment: (
|
endAdornment: <InputAdornment position="end">punk</InputAdornment>,
|
||||||
<InputAdornment position="end">punks</InputAdornment>
|
|
||||||
),
|
|
||||||
}}
|
}}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
@@ -303,9 +287,7 @@ export const BondForm = ({
|
|||||||
label="Mix Port"
|
label="Mix Port"
|
||||||
fullWidth
|
fullWidth
|
||||||
error={!!errors.mixPort}
|
error={!!errors.mixPort}
|
||||||
helperText={
|
helperText={errors.mixPort?.message && 'A valid port value is required'}
|
||||||
errors.mixPort?.message && 'A valid port value is required'
|
|
||||||
}
|
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
@@ -320,10 +302,7 @@ export const BondForm = ({
|
|||||||
label="Verloc Port"
|
label="Verloc Port"
|
||||||
fullWidth
|
fullWidth
|
||||||
error={!!errors.verlocPort}
|
error={!!errors.verlocPort}
|
||||||
helperText={
|
helperText={errors.verlocPort?.message && 'A valid port value is required'}
|
||||||
errors.verlocPort?.message &&
|
|
||||||
'A valid port value is required'
|
|
||||||
}
|
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
@@ -337,10 +316,7 @@ export const BondForm = ({
|
|||||||
label="HTTP API Port"
|
label="HTTP API Port"
|
||||||
fullWidth
|
fullWidth
|
||||||
error={!!errors.httpApiPort}
|
error={!!errors.httpApiPort}
|
||||||
helperText={
|
helperText={errors.httpApiPort?.message && 'A valid port value is required'}
|
||||||
errors.httpApiPort?.message &&
|
|
||||||
'A valid port value is required'
|
|
||||||
}
|
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
@@ -355,16 +331,23 @@ export const BondForm = ({
|
|||||||
label="client WS API Port"
|
label="client WS API Port"
|
||||||
fullWidth
|
fullWidth
|
||||||
error={!!errors.clientsPort}
|
error={!!errors.clientsPort}
|
||||||
helperText={
|
helperText={errors.clientsPort?.message && 'A valid port value is required'}
|
||||||
errors.clientsPort?.message &&
|
|
||||||
'A valid port value is required'
|
|
||||||
}
|
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
{fees && (
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<Typography sx={{ color: 'nym.info' }}>
|
||||||
|
{' '}
|
||||||
|
{`A bonding fee: ${
|
||||||
|
watchNodeType === EnumNodeType.mixnode ? fees.mixnode.amount : fees?.gateway?.amount
|
||||||
|
}`}
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
)}
|
||||||
</Grid>
|
</Grid>
|
||||||
</Box>
|
</Box>
|
||||||
<Box
|
<Box
|
||||||
@@ -373,7 +356,7 @@ export const BondForm = ({
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'flex-end',
|
justifyContent: 'flex-end',
|
||||||
borderTop: (theme) => `1px solid ${theme.palette.grey[200]}`,
|
borderTop: (theme) => `1px solid ${theme.palette.grey[200]}`,
|
||||||
bgcolor: (theme) => theme.palette.grey[50],
|
bgcolor: 'grey.100',
|
||||||
padding: 2,
|
padding: 2,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import React, { useContext } from 'react'
|
||||||
|
import { Box } from '@mui/system'
|
||||||
|
import { SuccessReponse, TransactionDetails } from '../../components'
|
||||||
|
import { ClientContext } from '../../context/main'
|
||||||
|
|
||||||
|
export const SuccessView: React.FC<{ details?: { amount: string; address: string } }> = ({ details }) => {
|
||||||
|
const { userBalance } = useContext(ClientContext)
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SuccessReponse
|
||||||
|
title="Bonding Complete"
|
||||||
|
subtitle="Sucessfully bonded to node with following details"
|
||||||
|
caption={`You current balance is: ${userBalance.balance?.printable_balance}`}
|
||||||
|
/>
|
||||||
|
{details && (
|
||||||
|
<Box sx={{ mt: 2 }}>
|
||||||
|
<TransactionDetails
|
||||||
|
details={[
|
||||||
|
{ primary: 'Node', secondary: details.address },
|
||||||
|
{ primary: 'Amount', secondary: `${details.amount} punk` },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -2,19 +2,18 @@ import React, { useContext, useEffect, useState } from 'react'
|
|||||||
import { Alert, Box, Button, CircularProgress } from '@mui/material'
|
import { Alert, Box, Button, CircularProgress } from '@mui/material'
|
||||||
import { BondForm } from './BondForm'
|
import { BondForm } from './BondForm'
|
||||||
import { NymCard } from '../../components'
|
import { NymCard } from '../../components'
|
||||||
import {
|
import { EnumRequestStatus, RequestStatus } from '../../components/RequestStatus'
|
||||||
EnumRequestStatus,
|
|
||||||
RequestStatus,
|
|
||||||
} from '../../components/RequestStatus'
|
|
||||||
import { Layout } from '../../layouts'
|
import { Layout } from '../../layouts'
|
||||||
import { getGasFee, unbond } from '../../requests'
|
import { getGasFee, unbond } from '../../requests'
|
||||||
import { TFee } from '../../types'
|
import { TFee } from '../../types'
|
||||||
import { useCheckOwnership } from '../../hooks/useCheckOwnership'
|
import { useCheckOwnership } from '../../hooks/useCheckOwnership'
|
||||||
import { ClientContext } from '../../context/main'
|
import { ClientContext } from '../../context/main'
|
||||||
|
import { SuccessView } from './SuccessView'
|
||||||
|
|
||||||
export const Bond = () => {
|
export const Bond = () => {
|
||||||
const [status, setStatus] = useState(EnumRequestStatus.initial)
|
const [status, setStatus] = useState(EnumRequestStatus.initial)
|
||||||
const [message, setMessage] = useState<string>()
|
const [error, setError] = useState<string>()
|
||||||
|
const [successDetails, setSuccessDetails] = useState<{ amount: string; address: string }>()
|
||||||
const [fees, setFees] = useState<TFee>()
|
const [fees, setFees] = useState<TFee>()
|
||||||
|
|
||||||
const { checkOwnership, ownership } = useCheckOwnership()
|
const { checkOwnership, ownership } = useCheckOwnership()
|
||||||
@@ -74,29 +73,24 @@ export const Bond = () => {
|
|||||||
<BondForm
|
<BondForm
|
||||||
fees={!ownership.hasOwnership ? fees : undefined}
|
fees={!ownership.hasOwnership ? fees : undefined}
|
||||||
onError={(e?: string) => {
|
onError={(e?: string) => {
|
||||||
setMessage(e)
|
setError(e)
|
||||||
setStatus(EnumRequestStatus.error)
|
setStatus(EnumRequestStatus.error)
|
||||||
}}
|
}}
|
||||||
onSuccess={(message?: string) => {
|
onSuccess={(details) => {
|
||||||
setMessage(message)
|
setSuccessDetails(details)
|
||||||
setStatus(EnumRequestStatus.success)
|
setStatus(EnumRequestStatus.success)
|
||||||
}}
|
}}
|
||||||
disabled={ownership?.hasOwnership}
|
disabled={ownership?.hasOwnership}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{(status === EnumRequestStatus.error ||
|
{(status === EnumRequestStatus.error || status === EnumRequestStatus.success) && (
|
||||||
status === EnumRequestStatus.success) && (
|
|
||||||
<>
|
<>
|
||||||
<RequestStatus
|
<RequestStatus
|
||||||
status={status}
|
status={status}
|
||||||
Success={
|
Success={<SuccessView details={successDetails} />}
|
||||||
<Alert severity="success" data-testid="bond-success">
|
|
||||||
Successfully bonded node
|
|
||||||
</Alert>
|
|
||||||
}
|
|
||||||
Error={
|
Error={
|
||||||
<Alert severity="error" data-testid="bond-error">
|
<Alert severity="error" data-testid="bond-error">
|
||||||
An error occurred with the request: {message}
|
An error occurred with the request: {error}
|
||||||
</Alert>
|
</Alert>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@@ -106,7 +100,7 @@ export const Bond = () => {
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'flex-end',
|
justifyContent: 'flex-end',
|
||||||
borderTop: (theme) => `1px solid ${theme.palette.grey[200]}`,
|
borderTop: (theme) => `1px solid ${theme.palette.grey[200]}`,
|
||||||
bgcolor: (theme) => theme.palette.grey[50],
|
bgcolor: 'grey.100',
|
||||||
padding: 2,
|
padding: 2,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -16,23 +16,15 @@ export const validationSchema = Yup.object().shape({
|
|||||||
}),
|
}),
|
||||||
sphinxKey: Yup.string()
|
sphinxKey: Yup.string()
|
||||||
.required('A sphinx key is required')
|
.required('A sphinx key is required')
|
||||||
.test(
|
.test('valid-sphinx-key', 'A valid sphinx key is required', function (value) {
|
||||||
'valid-sphinx-key',
|
return validateKey(value || '')
|
||||||
'A valid sphinx key is required',
|
}),
|
||||||
function (value) {
|
|
||||||
return validateKey(value || '')
|
|
||||||
}
|
|
||||||
),
|
|
||||||
amount: Yup.string()
|
amount: Yup.string()
|
||||||
.required('An amount is required')
|
.required('An amount is required')
|
||||||
.test(
|
.test('valid-amount', 'A valid amount is required (min 100 punk)', function (value) {
|
||||||
'valid-amount',
|
return validateAmount(value || '', '100000000')
|
||||||
'A valid amount is required (min 100 punks)',
|
// minimum amount needs to come from the backend - replace when available
|
||||||
function (value) {
|
}),
|
||||||
return validateAmount(value || '', '100000000')
|
|
||||||
// minimum amount needs to come from the backend - replace when available
|
|
||||||
}
|
|
||||||
),
|
|
||||||
|
|
||||||
host: Yup.string()
|
host: Yup.string()
|
||||||
.required('A host is required')
|
.required('A host is required')
|
||||||
@@ -48,13 +40,9 @@ export const validationSchema = Yup.object().shape({
|
|||||||
if (!!value) {
|
if (!!value) {
|
||||||
return Yup.string()
|
return Yup.string()
|
||||||
.required('A location is required')
|
.required('A location is required')
|
||||||
.test(
|
.test('valid-location', 'A valid version is required', function (value) {
|
||||||
'valid-location',
|
return !!value ? validateLocation(value) : false
|
||||||
'A valid version is required',
|
})
|
||||||
function (value) {
|
|
||||||
return !!value ? validateLocation(value) : false
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
return Yup.mixed().notRequired()
|
return Yup.mixed().notRequired()
|
||||||
}),
|
}),
|
||||||
@@ -75,11 +63,7 @@ export const validationSchema = Yup.object().shape({
|
|||||||
}),
|
}),
|
||||||
clientsPort: Yup.number()
|
clientsPort: Yup.number()
|
||||||
.required('A clients port is required')
|
.required('A clients port is required')
|
||||||
.test(
|
.test('valid-clients', 'A valid clients port is required', function (value) {
|
||||||
'valid-clients',
|
return !!value ? validateRawPort(value) : false
|
||||||
'A valid clients port is required',
|
}),
|
||||||
function (value) {
|
|
||||||
return !!value ? validateRawPort(value) : false
|
|
||||||
}
|
|
||||||
),
|
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,16 +1,5 @@
|
|||||||
import React, { useContext } from 'react'
|
import React, { useContext } from 'react'
|
||||||
import {
|
import { Box, Button, CircularProgress, FormControl, Grid, InputAdornment, TextField, Typography } from '@mui/material'
|
||||||
Alert,
|
|
||||||
Box,
|
|
||||||
Button,
|
|
||||||
CircularProgress,
|
|
||||||
FormControl,
|
|
||||||
Grid,
|
|
||||||
InputAdornment,
|
|
||||||
TextField,
|
|
||||||
Theme,
|
|
||||||
useTheme,
|
|
||||||
} from '@mui/material'
|
|
||||||
import { useForm } from 'react-hook-form'
|
import { useForm } from 'react-hook-form'
|
||||||
import { EnumNodeType, TFee } from '../../types'
|
import { EnumNodeType, TFee } from '../../types'
|
||||||
import { yupResolver } from '@hookform/resolvers/yup'
|
import { yupResolver } from '@hookform/resolvers/yup'
|
||||||
@@ -38,7 +27,7 @@ export const DelegateForm = ({
|
|||||||
}: {
|
}: {
|
||||||
fees: TFee
|
fees: TFee
|
||||||
onError: (message?: string) => void
|
onError: (message?: string) => void
|
||||||
onSuccess: (message?: string) => void
|
onSuccess: (details: { amount: string; address: string }) => void
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
@@ -71,9 +60,7 @@ export const DelegateForm = ({
|
|||||||
amount,
|
amount,
|
||||||
})
|
})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
onSuccess(
|
onSuccess({ amount: data.amount, address: res.target_address })
|
||||||
`Successfully delegated ${data.amount} punk to ${res.target_address}`,
|
|
||||||
)
|
|
||||||
userBalance.fetchBalance()
|
userBalance.fetchBalance()
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
@@ -86,13 +73,6 @@ export const DelegateForm = ({
|
|||||||
<FormControl fullWidth>
|
<FormControl fullWidth>
|
||||||
<Box sx={{ padding: [3, 5] }}>
|
<Box sx={{ padding: [3, 5] }}>
|
||||||
<Grid container spacing={3}>
|
<Grid container spacing={3}>
|
||||||
<Grid container item xs={12} justifyContent="space-between">
|
|
||||||
<Grid item>
|
|
||||||
<Alert severity="info" data-testid="fee-amount">
|
|
||||||
{`A fee of ${fees.mixnode.amount} PUNK will apply to this transaction`}
|
|
||||||
</Alert>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<TextField
|
<TextField
|
||||||
{...register('identity')}
|
{...register('identity')}
|
||||||
@@ -119,12 +99,13 @@ export const DelegateForm = ({
|
|||||||
error={!!errors.amount}
|
error={!!errors.amount}
|
||||||
helperText={errors?.amount?.message}
|
helperText={errors?.amount?.message}
|
||||||
InputProps={{
|
InputProps={{
|
||||||
endAdornment: (
|
endAdornment: <InputAdornment position="end">punk</InputAdornment>,
|
||||||
<InputAdornment position="end">punks</InputAdornment>
|
|
||||||
),
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<Typography sx={{ color: 'nym.info' }}>Fee for this transaction: {fees.mixnode.amount} punk</Typography>
|
||||||
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Box>
|
</Box>
|
||||||
<Box
|
<Box
|
||||||
@@ -133,7 +114,7 @@ export const DelegateForm = ({
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'flex-end',
|
justifyContent: 'flex-end',
|
||||||
borderTop: (theme) => `1px solid ${theme.palette.grey[200]}`,
|
borderTop: (theme) => `1px solid ${theme.palette.grey[200]}`,
|
||||||
background: (theme) => theme.palette.grey[50],
|
bgcolor: 'grey.100',
|
||||||
padding: 2,
|
padding: 2,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import React, { useContext } from 'react'
|
||||||
|
import { Box } from '@mui/system'
|
||||||
|
import { SuccessReponse, TransactionDetails } from '../../components'
|
||||||
|
import { ClientContext } from '../../context/main'
|
||||||
|
|
||||||
|
export const SuccessView: React.FC<{ details?: { amount: string; address: string } }> = ({ details }) => {
|
||||||
|
const { userBalance } = useContext(ClientContext)
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SuccessReponse
|
||||||
|
title="Delegation Complete"
|
||||||
|
subtitle="Sucessfully delegated to node with following details"
|
||||||
|
caption={`You current balance is: ${userBalance.balance?.printable_balance}`}
|
||||||
|
/>
|
||||||
|
{details && (
|
||||||
|
<Box sx={{ mt: 2 }}>
|
||||||
|
<TransactionDetails
|
||||||
|
details={[
|
||||||
|
{ primary: 'Node', secondary: details.address },
|
||||||
|
{ primary: 'Amount', secondary: `${details.amount} punk` },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -3,43 +3,32 @@ import { Alert, AlertTitle, Box, Button, CircularProgress } from '@mui/material'
|
|||||||
import { DelegateForm } from './DelegateForm'
|
import { DelegateForm } from './DelegateForm'
|
||||||
import { Layout } from '../../layouts'
|
import { Layout } from '../../layouts'
|
||||||
import { NymCard } from '../../components'
|
import { NymCard } from '../../components'
|
||||||
import {
|
import { EnumRequestStatus, RequestStatus } from '../../components/RequestStatus'
|
||||||
EnumRequestStatus,
|
|
||||||
RequestStatus,
|
|
||||||
} from '../../components/RequestStatus'
|
|
||||||
import { TFee } from '../../types'
|
import { TFee } from '../../types'
|
||||||
import { getGasFee } from '../../requests'
|
import { getGasFee } from '../../requests'
|
||||||
|
import { SuccessView } from './SuccessView'
|
||||||
|
|
||||||
export const Delegate = () => {
|
export const Delegate = () => {
|
||||||
const [status, setStatus] = useState<EnumRequestStatus>(
|
const [status, setStatus] = useState<EnumRequestStatus>(EnumRequestStatus.initial)
|
||||||
EnumRequestStatus.initial,
|
const [error, setError] = useState<string>()
|
||||||
)
|
const [successDetails, setSuccessDetails] = useState<{ amount: string; address: string }>()
|
||||||
const [message, setMessage] = useState<string>()
|
|
||||||
const [isLoading, setIsLoading] = useState(true)
|
const [isLoading, setIsLoading] = useState(true)
|
||||||
const [fees, setFees] = useState<TFee>()
|
const [fees, setFees] = useState<TFee>()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const getFees = async () => {
|
const getFees = async () => {
|
||||||
const mixnode = await getGasFee('DelegateToMixnode')
|
const mixnode = await getGasFee('DelegateToMixnode')
|
||||||
|
|
||||||
setFees({
|
setFees({
|
||||||
mixnode: mixnode,
|
mixnode: mixnode,
|
||||||
})
|
})
|
||||||
|
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
getFees()
|
getFees()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
<NymCard
|
<NymCard title="Delegate" subheader="Delegate to mixnode" noPadding data-testid="delegateCard">
|
||||||
title="Delegate"
|
|
||||||
subheader="Delegate to mixnode"
|
|
||||||
noPadding
|
|
||||||
data-testid="delegateCard"
|
|
||||||
>
|
|
||||||
{isLoading && (
|
{isLoading && (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@@ -57,11 +46,11 @@ export const Delegate = () => {
|
|||||||
fees={fees}
|
fees={fees}
|
||||||
onError={(message?: string) => {
|
onError={(message?: string) => {
|
||||||
setStatus(EnumRequestStatus.error)
|
setStatus(EnumRequestStatus.error)
|
||||||
setMessage(message)
|
setError(message)
|
||||||
}}
|
}}
|
||||||
onSuccess={(message?: string) => {
|
onSuccess={(details) => {
|
||||||
setStatus(EnumRequestStatus.success)
|
setStatus(EnumRequestStatus.success)
|
||||||
setMessage(message)
|
setSuccessDetails(details)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -73,15 +62,10 @@ export const Delegate = () => {
|
|||||||
<Alert severity="error" data-testid="delegate-error">
|
<Alert severity="error" data-testid="delegate-error">
|
||||||
<AlertTitle>Delegation failed</AlertTitle>
|
<AlertTitle>Delegation failed</AlertTitle>
|
||||||
An error occurred with the request:
|
An error occurred with the request:
|
||||||
<Box sx={{ wordBreak: 'break-word' }}>{message}</Box>
|
<Box sx={{ wordBreak: 'break-word' }}>{error}</Box>
|
||||||
</Alert>
|
|
||||||
}
|
|
||||||
Success={
|
|
||||||
<Alert severity="success" data-testid="delegate-success">
|
|
||||||
<AlertTitle>Delegation complete</AlertTitle>
|
|
||||||
<Box style={{ wordBreak: 'break-word' }}>{message}</Box>
|
|
||||||
</Alert>
|
</Alert>
|
||||||
}
|
}
|
||||||
|
Success={<SuccessView details={successDetails} />}
|
||||||
/>
|
/>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@@ -89,7 +73,7 @@ export const Delegate = () => {
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'flex-end',
|
justifyContent: 'flex-end',
|
||||||
borderTop: (theme) => `1px solid ${theme.palette.grey[200]}`,
|
borderTop: (theme) => `1px solid ${theme.palette.grey[200]}`,
|
||||||
background: (theme) => theme.palette.grey[50],
|
bgcolor: 'grey.100',
|
||||||
p: 2,
|
p: 2,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,18 +1,21 @@
|
|||||||
import React from 'react'
|
import React, { useContext } from 'react'
|
||||||
import { Box, Card, CircularProgress, Typography } from '@mui/material'
|
import { Box, CircularProgress, Link, Typography } from '@mui/material'
|
||||||
import { CheckCircleOutline } from '@mui/icons-material'
|
|
||||||
import { SendError } from './SendError'
|
import { SendError } from './SendError'
|
||||||
import { TauriTxResult } from '../../types/rust/tauritxresult'
|
import { ClientContext, urls } from '../../context/main'
|
||||||
|
import { SuccessReponse } from '../../components'
|
||||||
|
import { TransactionDetails } from '../../components/TransactionDetails'
|
||||||
|
import { TransactionDetails as TTransactionDetails } from '../../types'
|
||||||
|
|
||||||
export const SendConfirmation = ({
|
export const SendConfirmation = ({
|
||||||
data,
|
data,
|
||||||
error,
|
error,
|
||||||
isLoading,
|
isLoading,
|
||||||
}: {
|
}: {
|
||||||
data?: TauriTxResult['details']
|
data?: TTransactionDetails & { tx_hash: string }
|
||||||
error?: string
|
error?: string
|
||||||
isLoading: boolean
|
isLoading: boolean
|
||||||
}) => {
|
}) => {
|
||||||
|
const { userBalance, clientDetails } = useContext(ClientContext)
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@@ -27,53 +30,28 @@ export const SendConfirmation = ({
|
|||||||
{!isLoading && !!error && <SendError message={error} />}
|
{!isLoading && !!error && <SendError message={error} />}
|
||||||
{!isLoading && data && (
|
{!isLoading && data && (
|
||||||
<>
|
<>
|
||||||
<Box
|
<SuccessReponse
|
||||||
sx={{
|
title="Transaction Complete"
|
||||||
display: 'flex',
|
subtitle={
|
||||||
flexDirection: 'column',
|
<>
|
||||||
alignItems: 'center',
|
Check the transaction hash{' '}
|
||||||
justifyContent: 'center',
|
<Link href={`${urls.blockExplorer}/transactions/${data.tx_hash}`} target="_blank">
|
||||||
mb: 4,
|
here
|
||||||
}}
|
</Link>
|
||||||
>
|
</>
|
||||||
<CheckCircleOutline
|
}
|
||||||
sx={{
|
caption={
|
||||||
fontSize: 50,
|
userBalance.balance && (
|
||||||
color: 'success.main',
|
<Typography>Your current balance is: {userBalance.balance.printable_balance}</Typography>
|
||||||
mb: 1,
|
)
|
||||||
}}
|
}
|
||||||
/>
|
/>
|
||||||
<Typography data-testid="transaction-complete">
|
<TransactionDetails
|
||||||
Transaction complete
|
details={[
|
||||||
</Typography>
|
{ primary: 'Recipient', secondary: data.to_address },
|
||||||
</Box>
|
{ primary: 'Amount', secondary: data.amount.amount + ' punk' },
|
||||||
|
]}
|
||||||
<Card variant="outlined" sx={{ width: '100%', p: 2 }}>
|
/>
|
||||||
<Box sx={{ display: 'flex', mb: 2 }}>
|
|
||||||
<Box sx={{ width: '1/3' }}>
|
|
||||||
<Typography sx={{ color: (theme) => theme.palette.grey[600] }}>
|
|
||||||
Recipient
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
<Box sx={{ wordBreak: 'break-all' }}>
|
|
||||||
<Typography data-testid="to-address">
|
|
||||||
{data.to_address}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
<Box sx={{ display: 'flex' }}>
|
|
||||||
<Box sx={{ width: '33%' }}>
|
|
||||||
<Typography sx={{ color: (theme) => theme.palette.grey[600] }}>
|
|
||||||
Amount
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
<Box>
|
|
||||||
<Typography data-testid="send-amount">
|
|
||||||
{data.amount.amount + ' punks'}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
</Card>
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -1,26 +1,20 @@
|
|||||||
import React from 'react'
|
import React, { useContext } from 'react'
|
||||||
import { Grid, InputAdornment, TextField } from '@mui/material'
|
import { Grid, InputAdornment, TextField, Typography } from '@mui/material'
|
||||||
import { useFormContext } from 'react-hook-form'
|
import { useFormContext } from 'react-hook-form'
|
||||||
|
import { ClientContext } from '../../context/main'
|
||||||
|
|
||||||
export const SendForm = () => {
|
export const SendForm = ({ transferFee }: { transferFee?: string }) => {
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
formState: { errors },
|
formState: { errors },
|
||||||
} = useFormContext()
|
} = useFormContext()
|
||||||
|
const { clientDetails } = useContext(ClientContext)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid container spacing={3}>
|
<Grid container spacing={3}>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<TextField
|
<Typography variant="caption">Your address</Typography>
|
||||||
{...register('from')}
|
<Typography>{clientDetails?.client_address}</Typography>
|
||||||
required
|
|
||||||
variant="outlined"
|
|
||||||
id="from"
|
|
||||||
name="from"
|
|
||||||
label="From"
|
|
||||||
fullWidth
|
|
||||||
disabled={true}
|
|
||||||
/>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
@@ -37,7 +31,7 @@ export const SendForm = () => {
|
|||||||
helperText={errors.to?.message}
|
helperText={errors.to?.message}
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12} sm={6}>
|
<Grid item xs={12}>
|
||||||
<TextField
|
<TextField
|
||||||
{...register('amount')}
|
{...register('amount')}
|
||||||
required
|
required
|
||||||
@@ -49,10 +43,13 @@ export const SendForm = () => {
|
|||||||
error={!!errors.amount}
|
error={!!errors.amount}
|
||||||
helperText={errors.amount?.message}
|
helperText={errors.amount?.message}
|
||||||
InputProps={{
|
InputProps={{
|
||||||
endAdornment: <InputAdornment position="end">punks</InputAdornment>,
|
endAdornment: <InputAdornment position="end">punk</InputAdornment>,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<Typography sx={{ color: 'nym.info' }}>Fee for this transaction: {transferFee} punk</Typography>
|
||||||
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,85 +1,55 @@
|
|||||||
import React, { useEffect, useState } from 'react'
|
import React from 'react'
|
||||||
import {
|
import { Card, Divider, Grid, Typography } from '@mui/material'
|
||||||
Card,
|
|
||||||
CircularProgress,
|
|
||||||
Divider,
|
|
||||||
Grid,
|
|
||||||
Theme,
|
|
||||||
Typography,
|
|
||||||
} from '@mui/material'
|
|
||||||
import { useFormContext } from 'react-hook-form'
|
import { useFormContext } from 'react-hook-form'
|
||||||
import { getGasFee } from '../../requests'
|
|
||||||
|
|
||||||
export const SendReview = () => {
|
export const SendReview = ({ transferFee }: { transferFee?: string }) => {
|
||||||
const { getValues } = useFormContext()
|
const { getValues } = useFormContext()
|
||||||
const [transferFee, setTransferFee] = useState<string>()
|
|
||||||
const [isLoadingFee, setIsLoadingFee] = useState(true)
|
|
||||||
|
|
||||||
const values = getValues()
|
const values = getValues()
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const getFee = async () => {
|
|
||||||
const fee = await getGasFee('Send')
|
|
||||||
setTransferFee(fee.amount)
|
|
||||||
setIsLoadingFee(false)
|
|
||||||
}
|
|
||||||
getFee()
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
sx={{
|
sx={{
|
||||||
width: '100%',
|
width: '100%',
|
||||||
p: 2,
|
py: 3,
|
||||||
m: [3, 0],
|
px: 2,
|
||||||
|
my: 3,
|
||||||
|
mx: 0,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{isLoadingFee ? (
|
<Grid container spacing={2}>
|
||||||
<CircularProgress size={48} />
|
<Grid item xs={12}>
|
||||||
) : (
|
<SendReviewField title="From" subtitle={values.from} />
|
||||||
<Grid container spacing={2}>
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<SendReviewField title="From" subtitle={values.from} />
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<Divider light />
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<SendReviewField title="To" subtitle={values.to} />
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<Divider light />
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<SendReviewField title="Amount" subtitle={values.amount} />
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<Divider light />
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<SendReviewField
|
|
||||||
title="Transfer fee"
|
|
||||||
subtitle={transferFee + ' PUNK'}
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
)}
|
<Grid item xs={12}>
|
||||||
|
<Divider light />
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<SendReviewField title="To" subtitle={values.to} />
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<Divider light />
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<SendReviewField title="Amount" subtitle={values.amount + ' punk'} />
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<Divider light />
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<SendReviewField title="Transfer fee" subtitle={transferFee + ' punk'} info />
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
</Card>
|
</Card>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SendReviewField = ({
|
export const SendReviewField = ({ title, subtitle, info }: { title: string; subtitle?: string; info?: boolean }) => {
|
||||||
title,
|
|
||||||
subtitle,
|
|
||||||
}: {
|
|
||||||
title: string
|
|
||||||
subtitle?: string
|
|
||||||
}) => {
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Typography style={{ color: 'grey[600]' }}>{title}</Typography>
|
<Typography sx={{ color: info ? 'nym.info' : '' }}>{title}</Typography>
|
||||||
<Typography data-testid={title} style={{ wordBreak: 'break-all' }}>
|
<Typography data-testid={title} sx={{ color: info ? 'nym.info' : '', wordBreak: 'break-all' }}>
|
||||||
{subtitle}
|
{subtitle}
|
||||||
</Typography>
|
</Typography>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useContext, useState } from 'react'
|
import React, { useEffect, useContext, useState } from 'react'
|
||||||
import { useForm, FormProvider } from 'react-hook-form'
|
import { useForm, FormProvider } from 'react-hook-form'
|
||||||
import { yupResolver } from '@hookform/resolvers/yup'
|
import { yupResolver } from '@hookform/resolvers/yup'
|
||||||
import { Box, Button, Step, StepLabel, Stepper } from '@mui/material'
|
import { Box, Button, Step, StepLabel, Stepper } from '@mui/material'
|
||||||
@@ -7,8 +7,8 @@ import { SendReview } from './SendReview'
|
|||||||
import { SendConfirmation } from './SendConfirmation'
|
import { SendConfirmation } from './SendConfirmation'
|
||||||
import { ClientContext } from '../../context/main'
|
import { ClientContext } from '../../context/main'
|
||||||
import { validationSchema } from './validationSchema'
|
import { validationSchema } from './validationSchema'
|
||||||
import { TauriTxResult } from '../../types'
|
import { TauriTxResult, TransactionDetails } from '../../types'
|
||||||
import { majorToMinor, send } from '../../requests'
|
import { getGasFee, majorToMinor, send } from '../../requests'
|
||||||
import { checkHasEnoughFunds } from '../../utils'
|
import { checkHasEnoughFunds } from '../../utils'
|
||||||
|
|
||||||
const defaultValues = {
|
const defaultValues = {
|
||||||
@@ -21,24 +21,30 @@ export type TFormData = {
|
|||||||
amount: string
|
amount: string
|
||||||
memo: string
|
memo: string
|
||||||
to: string
|
to: string
|
||||||
from: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SendWizard = () => {
|
export const SendWizard = () => {
|
||||||
const [activeStep, setActiveStep] = useState(0)
|
const [activeStep, setActiveStep] = useState(0)
|
||||||
const [isLoading, setIsLoading] = useState(false)
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
const [requestError, setRequestError] = useState<string>()
|
const [requestError, setRequestError] = useState<string>()
|
||||||
const [confirmedData, setConfirmedData] = useState<TauriTxResult['details']>()
|
const [transferFee, setTransferFee] = useState<string>()
|
||||||
|
const [confirmedData, setConfirmedData] = useState<TransactionDetails & { tx_hash: string }>()
|
||||||
|
|
||||||
const { userBalance } = useContext(ClientContext)
|
const { userBalance } = useContext(ClientContext)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const getFee = async () => {
|
||||||
|
const fee = await getGasFee('Send')
|
||||||
|
setTransferFee(fee.amount)
|
||||||
|
}
|
||||||
|
getFee()
|
||||||
|
}, [])
|
||||||
|
|
||||||
const steps = ['Enter address', 'Review and send', 'Await confirmation']
|
const steps = ['Enter address', 'Review and send', 'Await confirmation']
|
||||||
|
|
||||||
const { clientDetails } = useContext(ClientContext)
|
|
||||||
const methods = useForm<TFormData>({
|
const methods = useForm<TFormData>({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
...defaultValues,
|
...defaultValues,
|
||||||
from: clientDetails?.client_address!,
|
|
||||||
},
|
},
|
||||||
resolver: yupResolver(validationSchema),
|
resolver: yupResolver(validationSchema),
|
||||||
})
|
})
|
||||||
@@ -74,12 +80,14 @@ export const SendWizard = () => {
|
|||||||
address: formState.to,
|
address: formState.to,
|
||||||
memo: formState.memo,
|
memo: formState.memo,
|
||||||
})
|
})
|
||||||
.then((res: any) => {
|
.then((res: TauriTxResult) => {
|
||||||
const { details } = res as TauriTxResult
|
const { details, tx_hash } = res
|
||||||
|
|
||||||
setActiveStep((s) => s + 1)
|
setActiveStep((s) => s + 1)
|
||||||
setConfirmedData({
|
setConfirmedData({
|
||||||
...details,
|
...details,
|
||||||
amount: { denom: 'Major', amount: formState.amount },
|
amount: { denom: 'Major', amount: formState.amount },
|
||||||
|
tx_hash,
|
||||||
})
|
})
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
userBalance.fetchBalance()
|
userBalance.fetchBalance()
|
||||||
@@ -98,9 +106,7 @@ export const SendWizard = () => {
|
|||||||
<Stepper
|
<Stepper
|
||||||
activeStep={activeStep}
|
activeStep={activeStep}
|
||||||
sx={{
|
sx={{
|
||||||
bgcolor: 'grey[50]',
|
p: 2,
|
||||||
pb: 0,
|
|
||||||
pt: 0,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{steps.map((s, i) => (
|
{steps.map((s, i) => (
|
||||||
@@ -115,19 +121,16 @@ export const SendWizard = () => {
|
|||||||
display: 'flex',
|
display: 'flex',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
p: [0, 3],
|
py: 0,
|
||||||
|
px: 3,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{activeStep === 0 ? (
|
{activeStep === 0 ? (
|
||||||
<SendForm />
|
<SendForm transferFee={transferFee} />
|
||||||
) : activeStep === 1 ? (
|
) : activeStep === 1 ? (
|
||||||
<SendReview />
|
<SendReview transferFee={transferFee} />
|
||||||
) : (
|
) : (
|
||||||
<SendConfirmation
|
<SendConfirmation data={confirmedData} isLoading={isLoading} error={requestError} />
|
||||||
data={confirmedData}
|
|
||||||
isLoading={isLoading}
|
|
||||||
error={requestError}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
<Box
|
<Box
|
||||||
@@ -136,17 +139,12 @@ export const SendWizard = () => {
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'flex-end',
|
justifyContent: 'flex-end',
|
||||||
borderTop: (theme) => `1px solid ${theme.palette.grey[200]}`,
|
borderTop: (theme) => `1px solid ${theme.palette.grey[200]}`,
|
||||||
background: (theme) => theme.palette.grey[50],
|
bgcolor: 'grey.50',
|
||||||
p: 2,
|
p: 2,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{activeStep === 1 && (
|
{activeStep === 1 && (
|
||||||
<Button
|
<Button disableElevation sx={{ mr: 1 }} onClick={handlePreviousStep} data-testid="back-button">
|
||||||
disableElevation
|
|
||||||
sx={{ mr: 1 }}
|
|
||||||
onClick={handlePreviousStep}
|
|
||||||
data-testid="back-button"
|
|
||||||
>
|
|
||||||
Back
|
Back
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
@@ -155,20 +153,8 @@ export const SendWizard = () => {
|
|||||||
color={activeStep > 0 ? 'primary' : 'inherit'}
|
color={activeStep > 0 ? 'primary' : 'inherit'}
|
||||||
disableElevation
|
disableElevation
|
||||||
data-testid="button"
|
data-testid="button"
|
||||||
onClick={
|
onClick={activeStep === 0 ? handleNextStep : activeStep === 1 ? handleSend : handleFinish}
|
||||||
activeStep === 0
|
disabled={!!(methods.formState.errors.amount || methods.formState.errors.to || isLoading)}
|
||||||
? handleNextStep
|
|
||||||
: activeStep === 1
|
|
||||||
? handleSend
|
|
||||||
: handleFinish
|
|
||||||
}
|
|
||||||
disabled={
|
|
||||||
!!(
|
|
||||||
methods.formState.errors.amount ||
|
|
||||||
methods.formState.errors.to ||
|
|
||||||
isLoading
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{activeStep === 0 ? 'Next' : activeStep === 1 ? 'Send' : 'Finish'}
|
{activeStep === 0 ? 'Next' : activeStep === 1 ? 'Send' : 'Finish'}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { Layout } from '../../layouts'
|
|||||||
export const Send = () => {
|
export const Send = () => {
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
<NymCard title="Send PUNK" noPadding>
|
<NymCard title="Send punk" noPadding>
|
||||||
<SendWizard />
|
<SendWizard />
|
||||||
</NymCard>
|
</NymCard>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export const SignIn = () => {
|
|||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
overflow: 'auto',
|
overflow: 'auto',
|
||||||
bgcolor: (theme) => theme.palette.nym.background.dark,
|
bgcolor: 'nym.background.dark',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
@@ -26,9 +26,7 @@ export const SignIn = () => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{showCreateAccount ? (
|
{showCreateAccount ? (
|
||||||
<CreateAccountContent
|
<CreateAccountContent showSignIn={() => setShowCreateAccount(false)} />
|
||||||
showSignIn={() => setShowCreateAccount(false)}
|
|
||||||
/>
|
|
||||||
) : (
|
) : (
|
||||||
<SignInContent showCreateAccount={() => setShowCreateAccount(true)} />
|
<SignInContent showCreateAccount={() => setShowCreateAccount(true)} />
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,22 +1,11 @@
|
|||||||
import React, { useContext, useState } from 'react'
|
import React, { useContext, useState } from 'react'
|
||||||
import {
|
import { Button, CircularProgress, Grid, Stack, Link, TextField, Typography, Alert, SvgIcon } from '@mui/material'
|
||||||
Button,
|
import { styled } from '@mui/material/styles'
|
||||||
CircularProgress,
|
import Logo from '../../images/logo-background.svg'
|
||||||
Grid,
|
|
||||||
Stack,
|
|
||||||
Link,
|
|
||||||
TextField,
|
|
||||||
Typography,
|
|
||||||
Alert,
|
|
||||||
} from '@mui/material'
|
|
||||||
import { styled } from '@mui/styles'
|
|
||||||
import logo from '../../images/logo-background.svg'
|
|
||||||
import { signInWithMnemonic } from '../../requests'
|
import { signInWithMnemonic } from '../../requests'
|
||||||
import { ClientContext } from '../../context/main'
|
import { ClientContext } from '../../context/main'
|
||||||
|
|
||||||
export const SignInContent: React.FC<{ showCreateAccount: () => void }> = ({
|
export const SignInContent: React.FC<{ showCreateAccount: () => void }> = ({ showCreateAccount }) => {
|
||||||
showCreateAccount,
|
|
||||||
}) => {
|
|
||||||
const [mnemonic, setMnemonic] = useState<string>('')
|
const [mnemonic, setMnemonic] = useState<string>('')
|
||||||
const [inputError, setInputError] = useState<string>()
|
const [inputError, setInputError] = useState<string>()
|
||||||
const [isLoading, setIsLoading] = useState(false)
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
@@ -41,17 +30,13 @@ export const SignInContent: React.FC<{ showCreateAccount: () => void }> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack spacing={3} alignItems="center" sx={{ width: '80%' }}>
|
<Stack spacing={3} alignItems="center" sx={{ width: '80%' }}>
|
||||||
<img src={logo} style={{ width: 80 }} />
|
<Logo width={80} />
|
||||||
<Typography sx={{ color: 'common.white' }}>
|
<Typography sx={{ color: 'common.white' }}>Enter Mnemonic and sign in</Typography>
|
||||||
Enter Mnemonic and sign in
|
|
||||||
</Typography>
|
|
||||||
<Grid container direction="column" spacing={3}>
|
<Grid container direction="column" spacing={3}>
|
||||||
<Grid item style={{ paddingTop: 0 }}>
|
<Grid item style={{ paddingTop: 0 }}>
|
||||||
<StyledInput
|
<StyledInput
|
||||||
value={mnemonic}
|
value={mnemonic}
|
||||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setMnemonic(e.target.value)}
|
||||||
setMnemonic(e.target.value)
|
|
||||||
}
|
|
||||||
size="medium"
|
size="medium"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
margin="normal"
|
margin="normal"
|
||||||
@@ -82,12 +67,7 @@ export const SignInContent: React.FC<{ showCreateAccount: () => void }> = ({
|
|||||||
</Grid>
|
</Grid>
|
||||||
{inputError && (
|
{inputError && (
|
||||||
<Grid item sx={{ mt: 1 }}>
|
<Grid item sx={{ mt: 1 }}>
|
||||||
<Alert
|
<Alert severity="error" variant="outlined" data-testid="error" sx={{ color: 'error.light' }}>
|
||||||
severity="error"
|
|
||||||
variant="outlined"
|
|
||||||
data-testid="error"
|
|
||||||
sx={{ color: 'error.light' }}
|
|
||||||
>
|
|
||||||
{inputError}
|
{inputError}
|
||||||
</Alert>
|
</Alert>
|
||||||
</Grid>
|
</Grid>
|
||||||
@@ -105,24 +85,22 @@ export const SignInContent: React.FC<{ showCreateAccount: () => void }> = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const StyledInput = styled((props) => <TextField {...props} />)(
|
const StyledInput = styled((props) => <TextField {...props} />)(({ theme }) => ({
|
||||||
({ theme }) => ({
|
'& input': {
|
||||||
'& input': {
|
color: theme.palette.nym.text.light,
|
||||||
color: theme.palette.nym.text.light,
|
},
|
||||||
|
'& label': {
|
||||||
|
color: theme.palette.nym.text.light,
|
||||||
|
},
|
||||||
|
'& label.Mui-focused': {
|
||||||
|
color: theme.palette.primary.main,
|
||||||
|
},
|
||||||
|
'& .MuiOutlinedInput-root': {
|
||||||
|
'& fieldset': {
|
||||||
|
borderColor: theme.palette.common.white,
|
||||||
},
|
},
|
||||||
'& label': {
|
'&:hover fieldset': {
|
||||||
color: theme.palette.nym.text.light,
|
borderColor: theme.palette.primary.main,
|
||||||
},
|
},
|
||||||
'& label.Mui-focused': {
|
},
|
||||||
color: theme.palette.primary.main,
|
}))
|
||||||
},
|
|
||||||
'& .MuiOutlinedInput-root': {
|
|
||||||
'& fieldset': {
|
|
||||||
borderColor: theme.palette.common.white,
|
|
||||||
},
|
|
||||||
'&:hover fieldset': {
|
|
||||||
borderColor: theme.palette.primary.main,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
FormControl,
|
FormControl,
|
||||||
Grid,
|
Grid,
|
||||||
TextField,
|
TextField,
|
||||||
|
Typography,
|
||||||
} from '@mui/material'
|
} from '@mui/material'
|
||||||
import { yupResolver } from '@hookform/resolvers/yup'
|
import { yupResolver } from '@hookform/resolvers/yup'
|
||||||
import { validationSchema } from './validationSchema'
|
import { validationSchema } from './validationSchema'
|
||||||
@@ -70,15 +71,8 @@ export const UndelegateForm = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<FormControl fullWidth>
|
<FormControl fullWidth>
|
||||||
<Box sx={{ p: [3, 5] }}>
|
<Box sx={{ p: 4 }}>
|
||||||
<Grid container spacing={3} direction="column">
|
<Grid container spacing={3} direction="column">
|
||||||
<Grid container item xs={12} justifyContent="space-between">
|
|
||||||
<Grid item>
|
|
||||||
<Alert severity="info" data-testid="fee-amount">
|
|
||||||
{`A fee of ${fees.mixnode.amount} PUNK will apply to this transaction`}
|
|
||||||
</Alert>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
@@ -87,11 +81,7 @@ export const UndelegateForm = ({
|
|||||||
<Autocomplete
|
<Autocomplete
|
||||||
value={field.value}
|
value={field.value}
|
||||||
onChange={(_, value) => setValue('identity', value || '')}
|
onChange={(_, value) => setValue('identity', value || '')}
|
||||||
options={
|
options={watchNodeType === EnumNodeType.mixnode ? delegations.mixnodes.delegated_nodes : []}
|
||||||
watchNodeType === EnumNodeType.mixnode
|
|
||||||
? delegations.mixnodes.delegated_nodes
|
|
||||||
: []
|
|
||||||
}
|
|
||||||
renderInput={(params) => (
|
renderInput={(params) => (
|
||||||
<TextField
|
<TextField
|
||||||
{...params}
|
{...params}
|
||||||
@@ -109,6 +99,9 @@ export const UndelegateForm = ({
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<Typography sx={{ color: 'nym.info' }}>Fee for this transaction: {fees.mixnode.amount} punk</Typography>
|
||||||
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Box>
|
</Box>
|
||||||
<Box
|
<Box
|
||||||
@@ -117,7 +110,7 @@ export const UndelegateForm = ({
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'flex-end',
|
justifyContent: 'flex-end',
|
||||||
borderTop: (theme) => `1px solid ${theme.palette.grey[200]}`,
|
borderTop: (theme) => `1px solid ${theme.palette.grey[200]}`,
|
||||||
background: (theme) => theme.palette.grey[50],
|
bgcolor: 'grey.100',
|
||||||
p: 2,
|
p: 2,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -2,10 +2,7 @@ import React, { useEffect, useState } from 'react'
|
|||||||
import { NymCard } from '../../components'
|
import { NymCard } from '../../components'
|
||||||
import { UndelegateForm } from './UndelegateForm'
|
import { UndelegateForm } from './UndelegateForm'
|
||||||
import { Layout } from '../../layouts'
|
import { Layout } from '../../layouts'
|
||||||
import {
|
import { EnumRequestStatus, RequestStatus } from '../../components/RequestStatus'
|
||||||
EnumRequestStatus,
|
|
||||||
RequestStatus,
|
|
||||||
} from '../../components/RequestStatus'
|
|
||||||
import { Alert, AlertTitle, Box, Button, CircularProgress } from '@mui/material'
|
import { Alert, AlertTitle, Box, Button, CircularProgress } from '@mui/material'
|
||||||
import { getGasFee, getReverseMixDelegations } from '../../requests'
|
import { getGasFee, getReverseMixDelegations } from '../../requests'
|
||||||
import { TFee, TDelegation } from '../../types'
|
import { TFee, TDelegation } from '../../types'
|
||||||
@@ -16,9 +13,7 @@ export type TDelegations = {
|
|||||||
|
|
||||||
export const Undelegate = () => {
|
export const Undelegate = () => {
|
||||||
const [message, setMessage] = useState<string>()
|
const [message, setMessage] = useState<string>()
|
||||||
const [status, setStatus] = useState<EnumRequestStatus>(
|
const [status, setStatus] = useState<EnumRequestStatus>(EnumRequestStatus.initial)
|
||||||
EnumRequestStatus.initial,
|
|
||||||
)
|
|
||||||
const [isLoading, setIsLoading] = useState(true)
|
const [isLoading, setIsLoading] = useState(true)
|
||||||
const [fees, setFees] = useState<TFee>()
|
const [fees, setFees] = useState<TFee>()
|
||||||
const [delegations, setDelegations] = useState<TDelegations>()
|
const [delegations, setDelegations] = useState<TDelegations>()
|
||||||
@@ -53,11 +48,7 @@ export const Undelegate = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
<NymCard
|
<NymCard title="Undelegate" subheader="Undelegate from a mixnode" noPadding>
|
||||||
title="Undelegate"
|
|
||||||
subheader="Undelegate from a mixnode"
|
|
||||||
noPadding
|
|
||||||
>
|
|
||||||
{isLoading && (
|
{isLoading && (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@@ -96,9 +87,7 @@ export const Undelegate = () => {
|
|||||||
Success={
|
Success={
|
||||||
<Alert severity="success">
|
<Alert severity="success">
|
||||||
{' '}
|
{' '}
|
||||||
<AlertTitle data-testid="undelegate-success">
|
<AlertTitle data-testid="undelegate-success">Undelegation complete</AlertTitle>
|
||||||
Undelegation complete
|
|
||||||
</AlertTitle>
|
|
||||||
{message}
|
{message}
|
||||||
</Alert>
|
</Alert>
|
||||||
}
|
}
|
||||||
@@ -109,12 +98,14 @@ export const Undelegate = () => {
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'flex-end',
|
justifyContent: 'flex-end',
|
||||||
borderTop: '1px solid grey[200]',
|
borderTop: '1px solid grey[200]',
|
||||||
bgcolor: 'grey[50]',
|
bgcolor: 'grey.100',
|
||||||
p: 2,
|
p: 2,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
data-testid="finish-button"
|
data-testid="finish-button"
|
||||||
|
variant="contained"
|
||||||
|
disableElevation
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setStatus(EnumRequestStatus.initial)
|
setStatus(EnumRequestStatus.initial)
|
||||||
initialize()
|
initialize()
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { SvgIcon, SvgIconProps } from '@mui/material'
|
||||||
|
|
||||||
|
export const Bond = (props: SvgIconProps) => {
|
||||||
|
return (
|
||||||
|
<SvgIcon {...props}>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M16 17C14.8954 17 14 16.1046 14 15L14 9C14 7.89543 14.8954 7 16 7L22 7C23.1046 7 24 7.89543 24 9L24 15C24 16.1046 23.1046 17 22 17L16 17ZM16 9L16 15L22 15L22 9L16 9Z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M2 17C0.89543 17 -1.00647e-07 16.1046 -2.24801e-07 15L-8.99206e-07 9C-1.02336e-06 7.89543 0.895429 7 2 7L8 7C9.10457 7 10 7.89543 10 9L10 15C10 16.1046 9.10457 17 8 17L2 17ZM2 9L2 15L8 15L8 9L2 9Z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M6 12C6 11.4477 6.44772 11 7 11H17C17.5523 11 18 11.4477 18 12C18 12.5523 17.5523 13 17 13H7C6.44772 13 6 12.5523 6 12Z"
|
||||||
|
/>
|
||||||
|
</SvgIcon>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { SvgIcon, SvgIconProps } from '@mui/material'
|
||||||
|
|
||||||
|
export const Delegate = (props: SvgIconProps) => {
|
||||||
|
return (
|
||||||
|
<SvgIcon {...props}>
|
||||||
|
<path d="M4 12V15H6V12H4ZM16 7L14.59 5.59L13 7.17V2H11V7.19L9.39 5.61L8 7L12 11L16 7ZM4 17H20V15H4V17Z" />
|
||||||
|
<path d="M20 21C20 21.5523 19.5523 22 19 22H5C4.44772 22 4 21.5523 4 21V20H20V21Z" />
|
||||||
|
<rect x="18" y="12" width="2" height="3" />
|
||||||
|
<rect x="18" y="17" width="2" height="3" />
|
||||||
|
<rect x="4" y="17" width="2" height="3" />
|
||||||
|
</SvgIcon>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
export * from './delegate'
|
||||||
|
export * from './undelegate'
|
||||||
|
export * from './bond'
|
||||||
|
export * from './unbond'
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { SvgIcon, SvgIconProps } from '@mui/material'
|
||||||
|
|
||||||
|
export const Unbond = (props: SvgIconProps) => {
|
||||||
|
return (
|
||||||
|
<SvgIcon {...props}>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M16 17C14.8954 17 14 16.1046 14 15L14 9C14 7.89543 14.8954 7 16 7L22 7C23.1046 7 24 7.89543 24 9L24 15C24 16.1046 23.1046 17 22 17L16 17ZM16 9L16 15L22 15L22 9L16 9Z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M2 17C0.89543 17 -1.00647e-07 16.1046 -2.24801e-07 15L-8.99206e-07 9C-1.02336e-06 7.89543 0.895429 7 2 7L8 7C9.10457 7 10 7.89543 10 9L10 15C10 16.1046 9.10457 17 8 17L2 17ZM2 9L2 15L8 15L8 9L2 9Z"
|
||||||
|
/>
|
||||||
|
</SvgIcon>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { SvgIcon, SvgIconProps } from '@mui/material'
|
||||||
|
|
||||||
|
export const Undelegate = (props: SvgIconProps) => {
|
||||||
|
return (
|
||||||
|
<SvgIcon {...props}>
|
||||||
|
<path d="M4 12V15H6V12H4ZM4 17H20V15H4V17Z" />
|
||||||
|
<path d="M20 21C20 21.5523 19.5523 22 19 22H5C4.44772 22 4 21.5523 4 21V20H20V21Z" />
|
||||||
|
<rect x="18" y="12" width="2" height="3" />
|
||||||
|
<rect x="18" y="17" width="2" height="3" />
|
||||||
|
<rect x="4" y="17" width="2" height="3" />
|
||||||
|
<path d="M9.41 7.41L8 6L12 2L16 6L14.61 7.39L13 5.81L13 11L11 11L11 5.83L9.41 7.41Z" />
|
||||||
|
</SvgIcon>
|
||||||
|
)
|
||||||
|
}
|
||||||
Vendored
+5
-8
@@ -1,10 +1,5 @@
|
|||||||
/* eslint-disable no-shadow,@typescript-eslint/no-unused-vars,@typescript-eslint/no-empty-interface */
|
/* eslint-disable no-shadow,@typescript-eslint/no-unused-vars,@typescript-eslint/no-empty-interface */
|
||||||
import {
|
import { Theme, ThemeOptions, Palette, PaletteOptions } from '@mui/material/styles'
|
||||||
Theme,
|
|
||||||
ThemeOptions,
|
|
||||||
Palette,
|
|
||||||
PaletteOptions,
|
|
||||||
} from '@mui/material/styles'
|
|
||||||
import { PaletteMode } from '@mui/material'
|
import { PaletteMode } from '@mui/material'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -34,9 +29,12 @@ declare module '@mui/material/styles' {
|
|||||||
*/
|
*/
|
||||||
interface NymPalette {
|
interface NymPalette {
|
||||||
highlight: string
|
highlight: string
|
||||||
|
success: string
|
||||||
|
info: string
|
||||||
background: { light: string; dark: string }
|
background: { light: string; dark: string }
|
||||||
text: {
|
text: {
|
||||||
light: string
|
light: string
|
||||||
|
dark: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,8 +63,7 @@ declare module '@mui/material/styles' {
|
|||||||
nym: NymPalette & NymWalletPalette
|
nym: NymPalette & NymWalletPalette
|
||||||
}
|
}
|
||||||
|
|
||||||
type NymPaletteAndNymWalletPaletteOptions =
|
type NymPaletteAndNymWalletPaletteOptions = Partial<NymPaletteAndNymWalletPalette>
|
||||||
Partial<NymPaletteAndNymWalletPalette>
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add anything not palette related to the theme here
|
* Add anything not palette related to the theme here
|
||||||
|
|||||||
@@ -21,9 +21,12 @@ import {
|
|||||||
const nymPalette: NymPalette = {
|
const nymPalette: NymPalette = {
|
||||||
/** emphasises important elements */
|
/** emphasises important elements */
|
||||||
highlight: '#FB6E4E',
|
highlight: '#FB6E4E',
|
||||||
|
success: '#21D073',
|
||||||
|
info: '#967FF0',
|
||||||
background: { light: '#F4F6F8', dark: '#121726' },
|
background: { light: '#F4F6F8', dark: '#121726' },
|
||||||
text: {
|
text: {
|
||||||
light: '#F2F2F2',
|
light: '#F2F2F2',
|
||||||
|
dark: '#121726',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,7 +51,7 @@ const lightMode: NymPaletteVariant = {
|
|||||||
paper: '#FFFFFF',
|
paper: '#FFFFFF',
|
||||||
},
|
},
|
||||||
text: {
|
text: {
|
||||||
main: '#666666',
|
main: '#121726',
|
||||||
},
|
},
|
||||||
topNav: {
|
topNav: {
|
||||||
background: '#111826',
|
background: '#111826',
|
||||||
@@ -80,6 +83,9 @@ const variantToMUIPalette = (variant: NymPaletteVariant): PaletteOptions => ({
|
|||||||
main: nymPalette.highlight,
|
main: nymPalette.highlight,
|
||||||
contrastText: '#fff',
|
contrastText: '#fff',
|
||||||
},
|
},
|
||||||
|
success: {
|
||||||
|
main: nymPalette.success,
|
||||||
|
},
|
||||||
background: {
|
background: {
|
||||||
default: variant.background.main,
|
default: variant.background.main,
|
||||||
paper: variant.background.paper,
|
paper: variant.background.paper,
|
||||||
@@ -145,9 +151,7 @@ export const getDesignTokens = (mode: PaletteMode): ThemeOptions => {
|
|||||||
const { palette } = createTheme({
|
const { palette } = createTheme({
|
||||||
palette: {
|
palette: {
|
||||||
mode,
|
mode,
|
||||||
...(mode === 'light'
|
...(mode === 'light' ? createLightModePalette() : createDarkModePalette()),
|
||||||
? createLightModePalette()
|
|
||||||
: createDarkModePalette()),
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -195,6 +199,18 @@ export const getDesignTokens = (mode: PaletteMode): ThemeOptions => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
MuiStepIcon: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
'&.Mui-completed': {
|
||||||
|
color: nymPalette.success,
|
||||||
|
},
|
||||||
|
'&.Mui-active': {
|
||||||
|
color: nymPalette.background.dark,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
palette,
|
palette,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ module.exports = {
|
|||||||
receivePageHeaderText: "Receive Nym",
|
receivePageHeaderText: "Receive Nym",
|
||||||
|
|
||||||
//sendPage
|
//sendPage
|
||||||
sendPunk: "Send PUNK",
|
sendPunk: "Send punk",
|
||||||
|
|
||||||
//homePage
|
//homePage
|
||||||
homePageErrorMnemonic: "Error parsing bip39 mnemonic",
|
homePageErrorMnemonic: "Error parsing bip39 mnemonic",
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ class WalletSend {
|
|||||||
return $("[data-testid='button");
|
return $("[data-testid='button");
|
||||||
}
|
}
|
||||||
get sendHeader() {
|
get sendHeader() {
|
||||||
return $("[data-testid='Send PUNK']");
|
return $("[data-testid='Send punk']");
|
||||||
}
|
}
|
||||||
get accountBalance() {
|
get accountBalance() {
|
||||||
return $("[data-testid='account-balance']");
|
return $("[data-testid='account-balance']");
|
||||||
|
|||||||
@@ -33,13 +33,18 @@ module.exports = {
|
|||||||
use: ['style-loader', 'css-loader'],
|
use: ['style-loader', 'css-loader'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.(png|jpe?g|gif|svg)$/i,
|
test: /\.(png|jpe?g|gif)$/i,
|
||||||
use: [
|
use: [
|
||||||
{
|
{
|
||||||
loader: 'file-loader',
|
loader: 'file-loader',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
test: /\.svg$/i,
|
||||||
|
issuer: /\.[jt]sx?$/,
|
||||||
|
use: ['@svgr/webpack'],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
|
|||||||
+1426
-38
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user