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
|
||||
}
|
||||
|
||||
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>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
|
||||
@@ -39,13 +39,10 @@ impl ValidatorDetails {
|
||||
}
|
||||
|
||||
pub fn default_validators() -> Vec<ValidatorDetails> {
|
||||
vec![
|
||||
ValidatorDetails::new(
|
||||
"https://testnet-milhon-validator1.nymtech.net",
|
||||
Some("https://testnet-milhon-validator1.nymtech.net/api"),
|
||||
),
|
||||
ValidatorDetails::new("https://testnet-milhon-validator2.nymtech.net", None),
|
||||
]
|
||||
vec![ValidatorDetails::new(
|
||||
"https://nova-validator.nymtech.net",
|
||||
Some("https://nova-validator.nymtech.net/api"),
|
||||
)]
|
||||
}
|
||||
|
||||
pub fn default_nymd_endpoints() -> Vec<Url> {
|
||||
@@ -62,9 +59,9 @@ pub fn default_api_endpoints() -> Vec<Url> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub const DEFAULT_MIXNET_CONTRACT_ADDRESS: &str = "punk10pyejy66429refv3g35g2t7am0was7yalwrzen";
|
||||
pub const DEFAULT_VESTING_CONTRACT_ADDRESS: &str = "";
|
||||
pub const REWARDING_VALIDATOR_ADDRESS: &str = "punk1v9qauwdq5terag6uvfsdytcs2d0sdmfdy7hgk3";
|
||||
pub const DEFAULT_MIXNET_CONTRACT_ADDRESS: &str = "tnym14hj2tavq8fpesdwxxcu44rty3hh90vhu5ksfyn";
|
||||
pub const DEFAULT_VESTING_CONTRACT_ADDRESS: &str = "tnym17p9rzwnnfxcjp32un9ug7yhhzgtkhvl9epkgzu";
|
||||
pub const REWARDING_VALIDATOR_ADDRESS: &str = "tnym1xwwst7fd30czjqlve0ww50t7x8mycs2sau5y85";
|
||||
|
||||
/// How much bandwidth (in bytes) one token can buy
|
||||
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] =
|
||||
hex_literal::hex!("9fEE3e28c17dbB87310A51F13C4fbf4331A6f102");
|
||||
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,
|
||||
// this would also need to be changed; It is currently tested against the json abi
|
||||
pub const ETH_EVENT_NAME: &str = "Burned";
|
||||
@@ -85,8 +82,8 @@ pub const ETH_BURN_FUNCTION_NAME: &str = "burnTokenForAccessCode";
|
||||
|
||||
/// Defaults Cosmos Hub/ATOM path
|
||||
pub const COSMOS_DERIVATION_PATH: &str = "m/44'/118'/0'/0/0";
|
||||
pub const BECH32_PREFIX: &str = "punk";
|
||||
pub const DENOM: &str = "upunk";
|
||||
pub const BECH32_PREFIX: &str = "tnym";
|
||||
pub const DENOM: &str = "utnym";
|
||||
// as set by validators in their configs
|
||||
// (note that the 'amount' postfix is relevant here as the full gas price also includes denom)
|
||||
pub const GAS_PRICE_AMOUNT: f64 = 0.025;
|
||||
|
||||
@@ -47,12 +47,15 @@ pub(crate) fn ensure_no_existing_bond(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(unreachable_code)]
|
||||
#[allow(unused_variables)]
|
||||
pub(crate) fn validate_node_identity_signature(
|
||||
deps: Deps,
|
||||
owner: &Addr,
|
||||
signature: String,
|
||||
identity: IdentityKeyRef,
|
||||
) -> Result<(), ContractError> {
|
||||
return Ok(());
|
||||
let owner_bytes = owner.as_bytes();
|
||||
|
||||
let mut identity_bytes = [0u8; 32];
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"@babel/plugin-transform-async-to-generator": "^7.14.5",
|
||||
"@babel/preset-env": "^7.15.0",
|
||||
"@babel/preset-react": "^7.14.5",
|
||||
"@svgr/webpack": "^6.1.1",
|
||||
"@tauri-apps/api": "^1.0.0-beta.6",
|
||||
"@tauri-apps/cli": "^1.0.0-beta.9",
|
||||
"@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 { Link, useLocation } from 'react-router-dom'
|
||||
import { List, ListItem, ListItemIcon, ListItemText } from '@mui/material'
|
||||
import {
|
||||
AccountBalanceWalletOutlined,
|
||||
ArrowBack,
|
||||
ArrowForward,
|
||||
AttachMoney,
|
||||
CancelOutlined,
|
||||
HowToVoteOutlined,
|
||||
MoneyOff,
|
||||
Description,
|
||||
Settings,
|
||||
} from '@mui/icons-material'
|
||||
import { AccountBalanceWalletOutlined, ArrowBack, ArrowForward, Description, Settings } from '@mui/icons-material'
|
||||
import { ADMIN_ADDRESS, ClientContext } from '../context/main'
|
||||
import { Bond, Delegate, Unbond, Undelegate } from '../svg-icons'
|
||||
|
||||
let routesSchema = [
|
||||
{
|
||||
@@ -33,22 +24,22 @@ let routesSchema = [
|
||||
{
|
||||
label: 'Bond',
|
||||
route: '/bond',
|
||||
Icon: AttachMoney,
|
||||
Icon: Bond,
|
||||
},
|
||||
{
|
||||
label: 'Unbond',
|
||||
route: '/unbond',
|
||||
Icon: MoneyOff,
|
||||
Icon: Unbond,
|
||||
},
|
||||
{
|
||||
label: 'Delegate',
|
||||
route: '/delegate',
|
||||
Icon: HowToVoteOutlined,
|
||||
Icon: Delegate,
|
||||
},
|
||||
{
|
||||
label: 'Undelegate',
|
||||
route: '/undelegate',
|
||||
Icon: CancelOutlined,
|
||||
Icon: Undelegate,
|
||||
},
|
||||
]
|
||||
|
||||
@@ -80,16 +71,14 @@ export const Nav = () => {
|
||||
<ListItemIcon
|
||||
sx={{
|
||||
minWidth: 30,
|
||||
color:
|
||||
location.pathname === route ? 'primary.main' : 'common.white',
|
||||
color: location.pathname === route ? 'primary.main' : 'common.white',
|
||||
}}
|
||||
>
|
||||
<Icon sx={{ fontSize: 20 }} />
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
sx={{
|
||||
color:
|
||||
location.pathname === route ? 'primary.main' : 'common.white',
|
||||
color: location.pathname === route ? 'primary.main' : 'common.white',
|
||||
}}
|
||||
primary={label}
|
||||
/>
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
import React from 'react'
|
||||
import {
|
||||
FormControl,
|
||||
FormControlLabel,
|
||||
FormLabel,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
} from '@mui/material'
|
||||
import { FormControl, FormControlLabel, FormLabel, Radio, RadioGroup } from '@mui/material'
|
||||
import { EnumNodeType } from '../types/global'
|
||||
|
||||
export const NodeTypeSelector = ({
|
||||
@@ -17,8 +11,7 @@ export const NodeTypeSelector = ({
|
||||
nodeType: EnumNodeType
|
||||
setNodeType: (nodeType: EnumNodeType) => void
|
||||
}) => {
|
||||
const handleNodeTypeChange = (e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
setNodeType(e.target.value as EnumNodeType)
|
||||
const handleNodeTypeChange = (e: React.ChangeEvent<HTMLInputElement>) => setNodeType(e.target.value as EnumNodeType)
|
||||
|
||||
return (
|
||||
<FormControl component="fieldset">
|
||||
@@ -32,14 +25,14 @@ export const NodeTypeSelector = ({
|
||||
>
|
||||
<FormControlLabel
|
||||
value={EnumNodeType.mixnode}
|
||||
control={<Radio />}
|
||||
control={<Radio color="default" />}
|
||||
label="Mixnode"
|
||||
data-testid="mix-node"
|
||||
disabled={disabled}
|
||||
/>
|
||||
<FormControlLabel
|
||||
value={EnumNodeType.gateway}
|
||||
control={<Radio />}
|
||||
control={<Radio color="default" />}
|
||||
data-testid="gate-way"
|
||||
label="Gateway"
|
||||
disabled={disabled}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react'
|
||||
import { Card, CardContent, CardHeader } from '@mui/material'
|
||||
import { styled } from '@mui/styles'
|
||||
import { styled } from '@mui/material/styles'
|
||||
|
||||
export const NymCard: React.FC<{
|
||||
title: string
|
||||
@@ -19,22 +19,20 @@ export const NymCard: React.FC<{
|
||||
action={Action}
|
||||
sx={{
|
||||
color: 'nym.background.dark',
|
||||
padding: 2.5,
|
||||
borderBottom: (theme) => `1px solid ${theme.palette.grey[100]}`,
|
||||
py: 2.5,
|
||||
px: 4,
|
||||
borderBottom: (theme) => `1px solid ${theme.palette.grey[200]}`,
|
||||
}}
|
||||
/>
|
||||
{noPadding ? (
|
||||
<CardContentNoPadding>{children}</CardContentNoPadding>
|
||||
) : (
|
||||
<CardContent>{children}</CardContent>
|
||||
)}
|
||||
{noPadding ? <CardContentNoPadding>{children}</CardContentNoPadding> : <CardContent>{children}</CardContent>}
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
const CardContentNoPadding = styled(CardContent)({
|
||||
const CardContentNoPadding = styled(CardContent)(({ theme }) => ({
|
||||
background: theme.palette.grey[50],
|
||||
padding: 0,
|
||||
'&:last-child': {
|
||||
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 './Error'
|
||||
export * from './Confirmation'
|
||||
export * from './CopyToClipboard'
|
||||
export * from './NymCard'
|
||||
export * from './Nav'
|
||||
export * from './NodeTypeSelector'
|
||||
export * from './RequestStatus'
|
||||
export * from './NoClientError'
|
||||
export * from './SuccessResponse'
|
||||
export * from './TransactionDetails'
|
||||
|
||||
@@ -20,11 +20,7 @@ type TClientContext = {
|
||||
|
||||
export const ClientContext = createContext({} as TClientContext)
|
||||
|
||||
export const ClientContextProvider = ({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) => {
|
||||
export const ClientContextProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
const [clientDetails, setClientDetails] = useState<TClientDetails>()
|
||||
const [showAdmin, setShowAdmin] = useState(false)
|
||||
const [mode, setMode] = useState<'light' | 'dark'>('light')
|
||||
@@ -41,8 +37,7 @@ export const ClientContextProvider = ({
|
||||
}
|
||||
}, [clientDetails, userBalance.fetchBalance])
|
||||
|
||||
const logIn = async (clientDetails: TSignInWithMnemonic) =>
|
||||
setClientDetails(clientDetails)
|
||||
const logIn = async (clientDetails: TSignInWithMnemonic) => setClientDetails(clientDetails)
|
||||
|
||||
const logOut = () => {
|
||||
setClientDetails(undefined)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { checkGatewayOwnership, checkMixnodeOwnership } from '../requests'
|
||||
import { EnumNodeType, TNodeOwnership } from '../types'
|
||||
|
||||
@@ -10,7 +10,7 @@ export const useCheckOwnership = () => {
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [error, setError] = useState<string>()
|
||||
|
||||
const checkOwnership = async () => {
|
||||
const checkOwnership = useCallback(async () => {
|
||||
const status = {} as TNodeOwnership
|
||||
|
||||
setIsLoading(true)
|
||||
@@ -33,7 +33,7 @@ export const useCheckOwnership = () => {
|
||||
} catch (e) {
|
||||
setError(e as string)
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
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"
|
||||
viewBox="0 0 5389.9 5389.9" style="enable-background:new 0 0 5389.9 5389.9;" xml:space="preserve">
|
||||
<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={{
|
||||
background: '#121726',
|
||||
overflow: 'auto',
|
||||
p: [4, 5],
|
||||
py: 4,
|
||||
px: 5,
|
||||
}}
|
||||
>
|
||||
<Box sx={{ mb: 3 }}>
|
||||
<img src={Logo} style={{ width: 45 }} />
|
||||
<Logo width={45} />
|
||||
</Box>
|
||||
|
||||
<Nav />
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React, { useContext } from 'react'
|
||||
import { Alert, Button, Grid, Link } from '@mui/material'
|
||||
import { Box } from '@mui/system'
|
||||
import React, { useContext, useEffect } from 'react'
|
||||
import { Alert, Button, Grid, Link, Typography } from '@mui/material'
|
||||
import { OpenInNew } from '@mui/icons-material'
|
||||
import { NymCard } from '../components'
|
||||
import { Layout } from '../layouts'
|
||||
@@ -10,6 +9,10 @@ import { ClientContext, urls } from '../context/main'
|
||||
export const Balance = () => {
|
||||
const { userBalance, clientDetails } = useContext(ClientContext)
|
||||
|
||||
useEffect(() => {
|
||||
userBalance.fetchBalance()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<NymCard title="Balance" data-testid="check-balance">
|
||||
@@ -21,10 +24,14 @@ export const Balance = () => {
|
||||
</Alert>
|
||||
)}
|
||||
{!userBalance.error && (
|
||||
<Box data-testid="refresh-success" sx={{ p: 2 }}>
|
||||
{'The current balance is ' +
|
||||
userBalance.balance?.printable_balance}
|
||||
</Box>
|
||||
<Typography
|
||||
data-testid="refresh-success"
|
||||
sx={{ p: 2, color: 'nym.background.dark' }}
|
||||
variant="h5"
|
||||
fontWeight="700"
|
||||
>
|
||||
{userBalance.balance?.printable_balance}
|
||||
</Typography>
|
||||
)}
|
||||
</Grid>
|
||||
<Grid item>
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
Grid,
|
||||
InputAdornment,
|
||||
TextField,
|
||||
Typography,
|
||||
} from '@mui/material'
|
||||
import { yupResolver } from '@hookform/resolvers/yup'
|
||||
import { useForm } from 'react-hook-form'
|
||||
@@ -81,7 +82,7 @@ export const BondForm = ({
|
||||
disabled: boolean
|
||||
fees?: { [EnumNodeType.mixnode]: Coin; [EnumNodeType.gateway]?: Coin }
|
||||
onError: (message?: string) => void
|
||||
onSuccess: (message?: string) => void
|
||||
onSuccess: (details: { address: string; amount: string }) => void
|
||||
}) => {
|
||||
const {
|
||||
register,
|
||||
@@ -98,10 +99,7 @@ export const BondForm = ({
|
||||
const { userBalance } = useContext(ClientContext)
|
||||
|
||||
const watchNodeType = watch('nodeType', defaultValues.nodeType)
|
||||
const watchAdvancedOptions = watch(
|
||||
'withAdvancedOptions',
|
||||
defaultValues.withAdvancedOptions,
|
||||
)
|
||||
const watchAdvancedOptions = watch('withAdvancedOptions', defaultValues.withAdvancedOptions)
|
||||
|
||||
const onSubmit = async (data: TBondFormFields) => {
|
||||
const hasEnoughFunds = await checkHasEnoughFunds(data.amount)
|
||||
@@ -115,7 +113,7 @@ export const BondForm = ({
|
||||
await bond({ type: data.nodeType, data: formattedData, amount })
|
||||
.then(() => {
|
||||
userBalance.fetchBalance()
|
||||
onSuccess(`Successfully bonded to ${data.identityKey}`)
|
||||
onSuccess({ address: data.identityKey, amount: data.amount })
|
||||
})
|
||||
.catch((e) => {
|
||||
onError(e)
|
||||
@@ -132,23 +130,11 @@ export const BondForm = ({
|
||||
nodeType={watchNodeType}
|
||||
setNodeType={(nodeType) => {
|
||||
setValue('nodeType', nodeType)
|
||||
if (nodeType === EnumNodeType.mixnode)
|
||||
setValue('location', undefined)
|
||||
if (nodeType === EnumNodeType.mixnode) setValue('location', undefined)
|
||||
}}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</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 item xs={12}>
|
||||
<TextField
|
||||
@@ -190,9 +176,7 @@ export const BondForm = ({
|
||||
error={!!errors.amount}
|
||||
helperText={errors.amount?.message}
|
||||
InputProps={{
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">punks</InputAdornment>
|
||||
),
|
||||
endAdornment: <InputAdornment position="end">punk</InputAdornment>,
|
||||
}}
|
||||
disabled={disabled}
|
||||
/>
|
||||
@@ -303,9 +287,7 @@ export const BondForm = ({
|
||||
label="Mix Port"
|
||||
fullWidth
|
||||
error={!!errors.mixPort}
|
||||
helperText={
|
||||
errors.mixPort?.message && 'A valid port value is required'
|
||||
}
|
||||
helperText={errors.mixPort?.message && 'A valid port value is required'}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</Grid>
|
||||
@@ -320,10 +302,7 @@ export const BondForm = ({
|
||||
label="Verloc Port"
|
||||
fullWidth
|
||||
error={!!errors.verlocPort}
|
||||
helperText={
|
||||
errors.verlocPort?.message &&
|
||||
'A valid port value is required'
|
||||
}
|
||||
helperText={errors.verlocPort?.message && 'A valid port value is required'}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</Grid>
|
||||
@@ -337,10 +316,7 @@ export const BondForm = ({
|
||||
label="HTTP API Port"
|
||||
fullWidth
|
||||
error={!!errors.httpApiPort}
|
||||
helperText={
|
||||
errors.httpApiPort?.message &&
|
||||
'A valid port value is required'
|
||||
}
|
||||
helperText={errors.httpApiPort?.message && 'A valid port value is required'}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</Grid>
|
||||
@@ -355,16 +331,23 @@ export const BondForm = ({
|
||||
label="client WS API Port"
|
||||
fullWidth
|
||||
error={!!errors.clientsPort}
|
||||
helperText={
|
||||
errors.clientsPort?.message &&
|
||||
'A valid port value is required'
|
||||
}
|
||||
helperText={errors.clientsPort?.message && 'A valid port value is required'}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</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>
|
||||
</Box>
|
||||
<Box
|
||||
@@ -373,7 +356,7 @@ export const BondForm = ({
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-end',
|
||||
borderTop: (theme) => `1px solid ${theme.palette.grey[200]}`,
|
||||
bgcolor: (theme) => theme.palette.grey[50],
|
||||
bgcolor: 'grey.100',
|
||||
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 { BondForm } from './BondForm'
|
||||
import { NymCard } from '../../components'
|
||||
import {
|
||||
EnumRequestStatus,
|
||||
RequestStatus,
|
||||
} from '../../components/RequestStatus'
|
||||
import { EnumRequestStatus, RequestStatus } from '../../components/RequestStatus'
|
||||
import { Layout } from '../../layouts'
|
||||
import { getGasFee, unbond } from '../../requests'
|
||||
import { TFee } from '../../types'
|
||||
import { useCheckOwnership } from '../../hooks/useCheckOwnership'
|
||||
import { ClientContext } from '../../context/main'
|
||||
import { SuccessView } from './SuccessView'
|
||||
|
||||
export const Bond = () => {
|
||||
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 { checkOwnership, ownership } = useCheckOwnership()
|
||||
@@ -74,29 +73,24 @@ export const Bond = () => {
|
||||
<BondForm
|
||||
fees={!ownership.hasOwnership ? fees : undefined}
|
||||
onError={(e?: string) => {
|
||||
setMessage(e)
|
||||
setError(e)
|
||||
setStatus(EnumRequestStatus.error)
|
||||
}}
|
||||
onSuccess={(message?: string) => {
|
||||
setMessage(message)
|
||||
onSuccess={(details) => {
|
||||
setSuccessDetails(details)
|
||||
setStatus(EnumRequestStatus.success)
|
||||
}}
|
||||
disabled={ownership?.hasOwnership}
|
||||
/>
|
||||
)}
|
||||
{(status === EnumRequestStatus.error ||
|
||||
status === EnumRequestStatus.success) && (
|
||||
{(status === EnumRequestStatus.error || status === EnumRequestStatus.success) && (
|
||||
<>
|
||||
<RequestStatus
|
||||
status={status}
|
||||
Success={
|
||||
<Alert severity="success" data-testid="bond-success">
|
||||
Successfully bonded node
|
||||
</Alert>
|
||||
}
|
||||
Success={<SuccessView details={successDetails} />}
|
||||
Error={
|
||||
<Alert severity="error" data-testid="bond-error">
|
||||
An error occurred with the request: {message}
|
||||
An error occurred with the request: {error}
|
||||
</Alert>
|
||||
}
|
||||
/>
|
||||
@@ -106,7 +100,7 @@ export const Bond = () => {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-end',
|
||||
borderTop: (theme) => `1px solid ${theme.palette.grey[200]}`,
|
||||
bgcolor: (theme) => theme.palette.grey[50],
|
||||
bgcolor: 'grey.100',
|
||||
padding: 2,
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -16,23 +16,15 @@ export const validationSchema = Yup.object().shape({
|
||||
}),
|
||||
sphinxKey: Yup.string()
|
||||
.required('A sphinx key is required')
|
||||
.test(
|
||||
'valid-sphinx-key',
|
||||
'A valid sphinx key is required',
|
||||
function (value) {
|
||||
return validateKey(value || '')
|
||||
}
|
||||
),
|
||||
.test('valid-sphinx-key', 'A valid sphinx key is required', function (value) {
|
||||
return validateKey(value || '')
|
||||
}),
|
||||
amount: Yup.string()
|
||||
.required('An amount is required')
|
||||
.test(
|
||||
'valid-amount',
|
||||
'A valid amount is required (min 100 punks)',
|
||||
function (value) {
|
||||
return validateAmount(value || '', '100000000')
|
||||
// minimum amount needs to come from the backend - replace when available
|
||||
}
|
||||
),
|
||||
.test('valid-amount', 'A valid amount is required (min 100 punk)', function (value) {
|
||||
return validateAmount(value || '', '100000000')
|
||||
// minimum amount needs to come from the backend - replace when available
|
||||
}),
|
||||
|
||||
host: Yup.string()
|
||||
.required('A host is required')
|
||||
@@ -48,13 +40,9 @@ export const validationSchema = Yup.object().shape({
|
||||
if (!!value) {
|
||||
return Yup.string()
|
||||
.required('A location is required')
|
||||
.test(
|
||||
'valid-location',
|
||||
'A valid version is required',
|
||||
function (value) {
|
||||
return !!value ? validateLocation(value) : false
|
||||
}
|
||||
)
|
||||
.test('valid-location', 'A valid version is required', function (value) {
|
||||
return !!value ? validateLocation(value) : false
|
||||
})
|
||||
}
|
||||
return Yup.mixed().notRequired()
|
||||
}),
|
||||
@@ -75,11 +63,7 @@ export const validationSchema = Yup.object().shape({
|
||||
}),
|
||||
clientsPort: Yup.number()
|
||||
.required('A clients port is required')
|
||||
.test(
|
||||
'valid-clients',
|
||||
'A valid clients port is required',
|
||||
function (value) {
|
||||
return !!value ? validateRawPort(value) : false
|
||||
}
|
||||
),
|
||||
.test('valid-clients', 'A valid clients port is required', function (value) {
|
||||
return !!value ? validateRawPort(value) : false
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -1,16 +1,5 @@
|
||||
import React, { useContext } from 'react'
|
||||
import {
|
||||
Alert,
|
||||
Box,
|
||||
Button,
|
||||
CircularProgress,
|
||||
FormControl,
|
||||
Grid,
|
||||
InputAdornment,
|
||||
TextField,
|
||||
Theme,
|
||||
useTheme,
|
||||
} from '@mui/material'
|
||||
import { Box, Button, CircularProgress, FormControl, Grid, InputAdornment, TextField, Typography } from '@mui/material'
|
||||
import { useForm } from 'react-hook-form'
|
||||
import { EnumNodeType, TFee } from '../../types'
|
||||
import { yupResolver } from '@hookform/resolvers/yup'
|
||||
@@ -38,7 +27,7 @@ export const DelegateForm = ({
|
||||
}: {
|
||||
fees: TFee
|
||||
onError: (message?: string) => void
|
||||
onSuccess: (message?: string) => void
|
||||
onSuccess: (details: { amount: string; address: string }) => void
|
||||
}) => {
|
||||
const {
|
||||
register,
|
||||
@@ -71,9 +60,7 @@ export const DelegateForm = ({
|
||||
amount,
|
||||
})
|
||||
.then((res) => {
|
||||
onSuccess(
|
||||
`Successfully delegated ${data.amount} punk to ${res.target_address}`,
|
||||
)
|
||||
onSuccess({ amount: data.amount, address: res.target_address })
|
||||
userBalance.fetchBalance()
|
||||
})
|
||||
.catch((e) => {
|
||||
@@ -86,13 +73,6 @@ export const DelegateForm = ({
|
||||
<FormControl fullWidth>
|
||||
<Box sx={{ padding: [3, 5] }}>
|
||||
<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}>
|
||||
<TextField
|
||||
{...register('identity')}
|
||||
@@ -119,12 +99,13 @@ export const DelegateForm = ({
|
||||
error={!!errors.amount}
|
||||
helperText={errors?.amount?.message}
|
||||
InputProps={{
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">punks</InputAdornment>
|
||||
),
|
||||
endAdornment: <InputAdornment position="end">punk</InputAdornment>,
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography sx={{ color: 'nym.info' }}>Fee for this transaction: {fees.mixnode.amount} punk</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
<Box
|
||||
@@ -133,7 +114,7 @@ export const DelegateForm = ({
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-end',
|
||||
borderTop: (theme) => `1px solid ${theme.palette.grey[200]}`,
|
||||
background: (theme) => theme.palette.grey[50],
|
||||
bgcolor: 'grey.100',
|
||||
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 { Layout } from '../../layouts'
|
||||
import { NymCard } from '../../components'
|
||||
import {
|
||||
EnumRequestStatus,
|
||||
RequestStatus,
|
||||
} from '../../components/RequestStatus'
|
||||
import { EnumRequestStatus, RequestStatus } from '../../components/RequestStatus'
|
||||
import { TFee } from '../../types'
|
||||
import { getGasFee } from '../../requests'
|
||||
import { SuccessView } from './SuccessView'
|
||||
|
||||
export const Delegate = () => {
|
||||
const [status, setStatus] = useState<EnumRequestStatus>(
|
||||
EnumRequestStatus.initial,
|
||||
)
|
||||
const [message, setMessage] = useState<string>()
|
||||
const [status, setStatus] = useState<EnumRequestStatus>(EnumRequestStatus.initial)
|
||||
const [error, setError] = useState<string>()
|
||||
const [successDetails, setSuccessDetails] = useState<{ amount: string; address: string }>()
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
const [fees, setFees] = useState<TFee>()
|
||||
|
||||
useEffect(() => {
|
||||
const getFees = async () => {
|
||||
const mixnode = await getGasFee('DelegateToMixnode')
|
||||
|
||||
setFees({
|
||||
mixnode: mixnode,
|
||||
})
|
||||
|
||||
setIsLoading(false)
|
||||
}
|
||||
|
||||
getFees()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<NymCard
|
||||
title="Delegate"
|
||||
subheader="Delegate to mixnode"
|
||||
noPadding
|
||||
data-testid="delegateCard"
|
||||
>
|
||||
<NymCard title="Delegate" subheader="Delegate to mixnode" noPadding data-testid="delegateCard">
|
||||
{isLoading && (
|
||||
<Box
|
||||
sx={{
|
||||
@@ -57,11 +46,11 @@ export const Delegate = () => {
|
||||
fees={fees}
|
||||
onError={(message?: string) => {
|
||||
setStatus(EnumRequestStatus.error)
|
||||
setMessage(message)
|
||||
setError(message)
|
||||
}}
|
||||
onSuccess={(message?: string) => {
|
||||
onSuccess={(details) => {
|
||||
setStatus(EnumRequestStatus.success)
|
||||
setMessage(message)
|
||||
setSuccessDetails(details)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
@@ -73,15 +62,10 @@ export const Delegate = () => {
|
||||
<Alert severity="error" data-testid="delegate-error">
|
||||
<AlertTitle>Delegation failed</AlertTitle>
|
||||
An error occurred with the request:
|
||||
<Box sx={{ wordBreak: 'break-word' }}>{message}</Box>
|
||||
</Alert>
|
||||
}
|
||||
Success={
|
||||
<Alert severity="success" data-testid="delegate-success">
|
||||
<AlertTitle>Delegation complete</AlertTitle>
|
||||
<Box style={{ wordBreak: 'break-word' }}>{message}</Box>
|
||||
<Box sx={{ wordBreak: 'break-word' }}>{error}</Box>
|
||||
</Alert>
|
||||
}
|
||||
Success={<SuccessView details={successDetails} />}
|
||||
/>
|
||||
<Box
|
||||
sx={{
|
||||
@@ -89,7 +73,7 @@ export const Delegate = () => {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-end',
|
||||
borderTop: (theme) => `1px solid ${theme.palette.grey[200]}`,
|
||||
background: (theme) => theme.palette.grey[50],
|
||||
bgcolor: 'grey.100',
|
||||
p: 2,
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
import React from 'react'
|
||||
import { Box, Card, CircularProgress, Typography } from '@mui/material'
|
||||
import { CheckCircleOutline } from '@mui/icons-material'
|
||||
import React, { useContext } from 'react'
|
||||
import { Box, CircularProgress, Link, Typography } from '@mui/material'
|
||||
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 = ({
|
||||
data,
|
||||
error,
|
||||
isLoading,
|
||||
}: {
|
||||
data?: TauriTxResult['details']
|
||||
data?: TTransactionDetails & { tx_hash: string }
|
||||
error?: string
|
||||
isLoading: boolean
|
||||
}) => {
|
||||
const { userBalance, clientDetails } = useContext(ClientContext)
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
@@ -27,53 +30,28 @@ export const SendConfirmation = ({
|
||||
{!isLoading && !!error && <SendError message={error} />}
|
||||
{!isLoading && data && (
|
||||
<>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
mb: 4,
|
||||
}}
|
||||
>
|
||||
<CheckCircleOutline
|
||||
sx={{
|
||||
fontSize: 50,
|
||||
color: 'success.main',
|
||||
mb: 1,
|
||||
}}
|
||||
/>
|
||||
<Typography data-testid="transaction-complete">
|
||||
Transaction complete
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<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>
|
||||
<SuccessReponse
|
||||
title="Transaction Complete"
|
||||
subtitle={
|
||||
<>
|
||||
Check the transaction hash{' '}
|
||||
<Link href={`${urls.blockExplorer}/transactions/${data.tx_hash}`} target="_blank">
|
||||
here
|
||||
</Link>
|
||||
</>
|
||||
}
|
||||
caption={
|
||||
userBalance.balance && (
|
||||
<Typography>Your current balance is: {userBalance.balance.printable_balance}</Typography>
|
||||
)
|
||||
}
|
||||
/>
|
||||
<TransactionDetails
|
||||
details={[
|
||||
{ primary: 'Recipient', secondary: data.to_address },
|
||||
{ primary: 'Amount', secondary: data.amount.amount + ' punk' },
|
||||
]}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
@@ -1,26 +1,20 @@
|
||||
import React from 'react'
|
||||
import { Grid, InputAdornment, TextField } from '@mui/material'
|
||||
import React, { useContext } from 'react'
|
||||
import { Grid, InputAdornment, TextField, Typography } from '@mui/material'
|
||||
import { useFormContext } from 'react-hook-form'
|
||||
import { ClientContext } from '../../context/main'
|
||||
|
||||
export const SendForm = () => {
|
||||
export const SendForm = ({ transferFee }: { transferFee?: string }) => {
|
||||
const {
|
||||
register,
|
||||
formState: { errors },
|
||||
} = useFormContext()
|
||||
const { clientDetails } = useContext(ClientContext)
|
||||
|
||||
return (
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12}>
|
||||
<TextField
|
||||
{...register('from')}
|
||||
required
|
||||
variant="outlined"
|
||||
id="from"
|
||||
name="from"
|
||||
label="From"
|
||||
fullWidth
|
||||
disabled={true}
|
||||
/>
|
||||
<Typography variant="caption">Your address</Typography>
|
||||
<Typography>{clientDetails?.client_address}</Typography>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12}>
|
||||
@@ -37,7 +31,7 @@ export const SendForm = () => {
|
||||
helperText={errors.to?.message}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6}>
|
||||
<Grid item xs={12}>
|
||||
<TextField
|
||||
{...register('amount')}
|
||||
required
|
||||
@@ -49,10 +43,13 @@ export const SendForm = () => {
|
||||
error={!!errors.amount}
|
||||
helperText={errors.amount?.message}
|
||||
InputProps={{
|
||||
endAdornment: <InputAdornment position="end">punks</InputAdornment>,
|
||||
endAdornment: <InputAdornment position="end">punk</InputAdornment>,
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography sx={{ color: 'nym.info' }}>Fee for this transaction: {transferFee} punk</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,85 +1,55 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import {
|
||||
Card,
|
||||
CircularProgress,
|
||||
Divider,
|
||||
Grid,
|
||||
Theme,
|
||||
Typography,
|
||||
} from '@mui/material'
|
||||
import React from 'react'
|
||||
import { Card, Divider, Grid, Typography } from '@mui/material'
|
||||
import { useFormContext } from 'react-hook-form'
|
||||
import { getGasFee } from '../../requests'
|
||||
|
||||
export const SendReview = () => {
|
||||
export const SendReview = ({ transferFee }: { transferFee?: string }) => {
|
||||
const { getValues } = useFormContext()
|
||||
const [transferFee, setTransferFee] = useState<string>()
|
||||
const [isLoadingFee, setIsLoadingFee] = useState(true)
|
||||
|
||||
const values = getValues()
|
||||
|
||||
useEffect(() => {
|
||||
const getFee = async () => {
|
||||
const fee = await getGasFee('Send')
|
||||
setTransferFee(fee.amount)
|
||||
setIsLoadingFee(false)
|
||||
}
|
||||
getFee()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Card
|
||||
variant="outlined"
|
||||
sx={{
|
||||
width: '100%',
|
||||
p: 2,
|
||||
m: [3, 0],
|
||||
py: 3,
|
||||
px: 2,
|
||||
my: 3,
|
||||
mx: 0,
|
||||
}}
|
||||
>
|
||||
{isLoadingFee ? (
|
||||
<CircularProgress size={48} />
|
||||
) : (
|
||||
<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 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 + ' punk'} />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Divider light />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<SendReviewField title="Transfer fee" subtitle={transferFee + ' punk'} info />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export const SendReviewField = ({
|
||||
title,
|
||||
subtitle,
|
||||
}: {
|
||||
title: string
|
||||
subtitle?: string
|
||||
}) => {
|
||||
export const SendReviewField = ({ title, subtitle, info }: { title: string; subtitle?: string; info?: boolean }) => {
|
||||
return (
|
||||
<>
|
||||
<Typography style={{ color: 'grey[600]' }}>{title}</Typography>
|
||||
<Typography data-testid={title} style={{ wordBreak: 'break-all' }}>
|
||||
<Typography sx={{ color: info ? 'nym.info' : '' }}>{title}</Typography>
|
||||
<Typography data-testid={title} sx={{ color: info ? 'nym.info' : '', wordBreak: 'break-all' }}>
|
||||
{subtitle}
|
||||
</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 { yupResolver } from '@hookform/resolvers/yup'
|
||||
import { Box, Button, Step, StepLabel, Stepper } from '@mui/material'
|
||||
@@ -7,8 +7,8 @@ import { SendReview } from './SendReview'
|
||||
import { SendConfirmation } from './SendConfirmation'
|
||||
import { ClientContext } from '../../context/main'
|
||||
import { validationSchema } from './validationSchema'
|
||||
import { TauriTxResult } from '../../types'
|
||||
import { majorToMinor, send } from '../../requests'
|
||||
import { TauriTxResult, TransactionDetails } from '../../types'
|
||||
import { getGasFee, majorToMinor, send } from '../../requests'
|
||||
import { checkHasEnoughFunds } from '../../utils'
|
||||
|
||||
const defaultValues = {
|
||||
@@ -21,24 +21,30 @@ export type TFormData = {
|
||||
amount: string
|
||||
memo: string
|
||||
to: string
|
||||
from: string
|
||||
}
|
||||
|
||||
export const SendWizard = () => {
|
||||
const [activeStep, setActiveStep] = useState(0)
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
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)
|
||||
|
||||
useEffect(() => {
|
||||
const getFee = async () => {
|
||||
const fee = await getGasFee('Send')
|
||||
setTransferFee(fee.amount)
|
||||
}
|
||||
getFee()
|
||||
}, [])
|
||||
|
||||
const steps = ['Enter address', 'Review and send', 'Await confirmation']
|
||||
|
||||
const { clientDetails } = useContext(ClientContext)
|
||||
const methods = useForm<TFormData>({
|
||||
defaultValues: {
|
||||
...defaultValues,
|
||||
from: clientDetails?.client_address!,
|
||||
},
|
||||
resolver: yupResolver(validationSchema),
|
||||
})
|
||||
@@ -74,12 +80,14 @@ export const SendWizard = () => {
|
||||
address: formState.to,
|
||||
memo: formState.memo,
|
||||
})
|
||||
.then((res: any) => {
|
||||
const { details } = res as TauriTxResult
|
||||
.then((res: TauriTxResult) => {
|
||||
const { details, tx_hash } = res
|
||||
|
||||
setActiveStep((s) => s + 1)
|
||||
setConfirmedData({
|
||||
...details,
|
||||
amount: { denom: 'Major', amount: formState.amount },
|
||||
tx_hash,
|
||||
})
|
||||
setIsLoading(false)
|
||||
userBalance.fetchBalance()
|
||||
@@ -98,9 +106,7 @@ export const SendWizard = () => {
|
||||
<Stepper
|
||||
activeStep={activeStep}
|
||||
sx={{
|
||||
bgcolor: 'grey[50]',
|
||||
pb: 0,
|
||||
pt: 0,
|
||||
p: 2,
|
||||
}}
|
||||
>
|
||||
{steps.map((s, i) => (
|
||||
@@ -115,19 +121,16 @@ export const SendWizard = () => {
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
p: [0, 3],
|
||||
py: 0,
|
||||
px: 3,
|
||||
}}
|
||||
>
|
||||
{activeStep === 0 ? (
|
||||
<SendForm />
|
||||
<SendForm transferFee={transferFee} />
|
||||
) : activeStep === 1 ? (
|
||||
<SendReview />
|
||||
<SendReview transferFee={transferFee} />
|
||||
) : (
|
||||
<SendConfirmation
|
||||
data={confirmedData}
|
||||
isLoading={isLoading}
|
||||
error={requestError}
|
||||
/>
|
||||
<SendConfirmation data={confirmedData} isLoading={isLoading} error={requestError} />
|
||||
)}
|
||||
</Box>
|
||||
<Box
|
||||
@@ -136,17 +139,12 @@ export const SendWizard = () => {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-end',
|
||||
borderTop: (theme) => `1px solid ${theme.palette.grey[200]}`,
|
||||
background: (theme) => theme.palette.grey[50],
|
||||
bgcolor: 'grey.50',
|
||||
p: 2,
|
||||
}}
|
||||
>
|
||||
{activeStep === 1 && (
|
||||
<Button
|
||||
disableElevation
|
||||
sx={{ mr: 1 }}
|
||||
onClick={handlePreviousStep}
|
||||
data-testid="back-button"
|
||||
>
|
||||
<Button disableElevation sx={{ mr: 1 }} onClick={handlePreviousStep} data-testid="back-button">
|
||||
Back
|
||||
</Button>
|
||||
)}
|
||||
@@ -155,20 +153,8 @@ export const SendWizard = () => {
|
||||
color={activeStep > 0 ? 'primary' : 'inherit'}
|
||||
disableElevation
|
||||
data-testid="button"
|
||||
onClick={
|
||||
activeStep === 0
|
||||
? handleNextStep
|
||||
: activeStep === 1
|
||||
? handleSend
|
||||
: handleFinish
|
||||
}
|
||||
disabled={
|
||||
!!(
|
||||
methods.formState.errors.amount ||
|
||||
methods.formState.errors.to ||
|
||||
isLoading
|
||||
)
|
||||
}
|
||||
onClick={activeStep === 0 ? handleNextStep : activeStep === 1 ? handleSend : handleFinish}
|
||||
disabled={!!(methods.formState.errors.amount || methods.formState.errors.to || isLoading)}
|
||||
>
|
||||
{activeStep === 0 ? 'Next' : activeStep === 1 ? 'Send' : 'Finish'}
|
||||
</Button>
|
||||
|
||||
@@ -6,7 +6,7 @@ import { Layout } from '../../layouts'
|
||||
export const Send = () => {
|
||||
return (
|
||||
<Layout>
|
||||
<NymCard title="Send PUNK" noPadding>
|
||||
<NymCard title="Send punk" noPadding>
|
||||
<SendWizard />
|
||||
</NymCard>
|
||||
</Layout>
|
||||
|
||||
@@ -14,7 +14,7 @@ export const SignIn = () => {
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
overflow: 'auto',
|
||||
bgcolor: (theme) => theme.palette.nym.background.dark,
|
||||
bgcolor: 'nym.background.dark',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
@@ -26,9 +26,7 @@ export const SignIn = () => {
|
||||
}}
|
||||
>
|
||||
{showCreateAccount ? (
|
||||
<CreateAccountContent
|
||||
showSignIn={() => setShowCreateAccount(false)}
|
||||
/>
|
||||
<CreateAccountContent showSignIn={() => setShowCreateAccount(false)} />
|
||||
) : (
|
||||
<SignInContent showCreateAccount={() => setShowCreateAccount(true)} />
|
||||
)}
|
||||
|
||||
@@ -1,22 +1,11 @@
|
||||
import React, { useContext, useState } from 'react'
|
||||
import {
|
||||
Button,
|
||||
CircularProgress,
|
||||
Grid,
|
||||
Stack,
|
||||
Link,
|
||||
TextField,
|
||||
Typography,
|
||||
Alert,
|
||||
} from '@mui/material'
|
||||
import { styled } from '@mui/styles'
|
||||
import logo from '../../images/logo-background.svg'
|
||||
import { Button, CircularProgress, Grid, Stack, Link, TextField, Typography, Alert, SvgIcon } from '@mui/material'
|
||||
import { styled } from '@mui/material/styles'
|
||||
import Logo from '../../images/logo-background.svg'
|
||||
import { signInWithMnemonic } from '../../requests'
|
||||
import { ClientContext } from '../../context/main'
|
||||
|
||||
export const SignInContent: React.FC<{ showCreateAccount: () => void }> = ({
|
||||
showCreateAccount,
|
||||
}) => {
|
||||
export const SignInContent: React.FC<{ showCreateAccount: () => void }> = ({ showCreateAccount }) => {
|
||||
const [mnemonic, setMnemonic] = useState<string>('')
|
||||
const [inputError, setInputError] = useState<string>()
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
@@ -41,17 +30,13 @@ export const SignInContent: React.FC<{ showCreateAccount: () => void }> = ({
|
||||
|
||||
return (
|
||||
<Stack spacing={3} alignItems="center" sx={{ width: '80%' }}>
|
||||
<img src={logo} style={{ width: 80 }} />
|
||||
<Typography sx={{ color: 'common.white' }}>
|
||||
Enter Mnemonic and sign in
|
||||
</Typography>
|
||||
<Logo width={80} />
|
||||
<Typography sx={{ color: 'common.white' }}>Enter Mnemonic and sign in</Typography>
|
||||
<Grid container direction="column" spacing={3}>
|
||||
<Grid item style={{ paddingTop: 0 }}>
|
||||
<StyledInput
|
||||
value={mnemonic}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
setMnemonic(e.target.value)
|
||||
}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setMnemonic(e.target.value)}
|
||||
size="medium"
|
||||
variant="outlined"
|
||||
margin="normal"
|
||||
@@ -82,12 +67,7 @@ export const SignInContent: React.FC<{ showCreateAccount: () => void }> = ({
|
||||
</Grid>
|
||||
{inputError && (
|
||||
<Grid item sx={{ mt: 1 }}>
|
||||
<Alert
|
||||
severity="error"
|
||||
variant="outlined"
|
||||
data-testid="error"
|
||||
sx={{ color: 'error.light' }}
|
||||
>
|
||||
<Alert severity="error" variant="outlined" data-testid="error" sx={{ color: 'error.light' }}>
|
||||
{inputError}
|
||||
</Alert>
|
||||
</Grid>
|
||||
@@ -105,24 +85,22 @@ export const SignInContent: React.FC<{ showCreateAccount: () => void }> = ({
|
||||
)
|
||||
}
|
||||
|
||||
const StyledInput = styled((props) => <TextField {...props} />)(
|
||||
({ theme }) => ({
|
||||
'& input': {
|
||||
color: theme.palette.nym.text.light,
|
||||
const StyledInput = styled((props) => <TextField {...props} />)(({ theme }) => ({
|
||||
'& input': {
|
||||
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': {
|
||||
color: theme.palette.nym.text.light,
|
||||
'&:hover fieldset': {
|
||||
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,
|
||||
Grid,
|
||||
TextField,
|
||||
Typography,
|
||||
} from '@mui/material'
|
||||
import { yupResolver } from '@hookform/resolvers/yup'
|
||||
import { validationSchema } from './validationSchema'
|
||||
@@ -70,15 +71,8 @@ export const UndelegateForm = ({
|
||||
|
||||
return (
|
||||
<FormControl fullWidth>
|
||||
<Box sx={{ p: [3, 5] }}>
|
||||
<Box sx={{ p: 4 }}>
|
||||
<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}>
|
||||
<Controller
|
||||
control={control}
|
||||
@@ -87,11 +81,7 @@ export const UndelegateForm = ({
|
||||
<Autocomplete
|
||||
value={field.value}
|
||||
onChange={(_, value) => setValue('identity', value || '')}
|
||||
options={
|
||||
watchNodeType === EnumNodeType.mixnode
|
||||
? delegations.mixnodes.delegated_nodes
|
||||
: []
|
||||
}
|
||||
options={watchNodeType === EnumNodeType.mixnode ? delegations.mixnodes.delegated_nodes : []}
|
||||
renderInput={(params) => (
|
||||
<TextField
|
||||
{...params}
|
||||
@@ -109,6 +99,9 @@ export const UndelegateForm = ({
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography sx={{ color: 'nym.info' }}>Fee for this transaction: {fees.mixnode.amount} punk</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
<Box
|
||||
@@ -117,7 +110,7 @@ export const UndelegateForm = ({
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-end',
|
||||
borderTop: (theme) => `1px solid ${theme.palette.grey[200]}`,
|
||||
background: (theme) => theme.palette.grey[50],
|
||||
bgcolor: 'grey.100',
|
||||
p: 2,
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -2,10 +2,7 @@ import React, { useEffect, useState } from 'react'
|
||||
import { NymCard } from '../../components'
|
||||
import { UndelegateForm } from './UndelegateForm'
|
||||
import { Layout } from '../../layouts'
|
||||
import {
|
||||
EnumRequestStatus,
|
||||
RequestStatus,
|
||||
} from '../../components/RequestStatus'
|
||||
import { EnumRequestStatus, RequestStatus } from '../../components/RequestStatus'
|
||||
import { Alert, AlertTitle, Box, Button, CircularProgress } from '@mui/material'
|
||||
import { getGasFee, getReverseMixDelegations } from '../../requests'
|
||||
import { TFee, TDelegation } from '../../types'
|
||||
@@ -16,9 +13,7 @@ export type TDelegations = {
|
||||
|
||||
export const Undelegate = () => {
|
||||
const [message, setMessage] = useState<string>()
|
||||
const [status, setStatus] = useState<EnumRequestStatus>(
|
||||
EnumRequestStatus.initial,
|
||||
)
|
||||
const [status, setStatus] = useState<EnumRequestStatus>(EnumRequestStatus.initial)
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
const [fees, setFees] = useState<TFee>()
|
||||
const [delegations, setDelegations] = useState<TDelegations>()
|
||||
@@ -53,11 +48,7 @@ export const Undelegate = () => {
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<NymCard
|
||||
title="Undelegate"
|
||||
subheader="Undelegate from a mixnode"
|
||||
noPadding
|
||||
>
|
||||
<NymCard title="Undelegate" subheader="Undelegate from a mixnode" noPadding>
|
||||
{isLoading && (
|
||||
<Box
|
||||
sx={{
|
||||
@@ -96,9 +87,7 @@ export const Undelegate = () => {
|
||||
Success={
|
||||
<Alert severity="success">
|
||||
{' '}
|
||||
<AlertTitle data-testid="undelegate-success">
|
||||
Undelegation complete
|
||||
</AlertTitle>
|
||||
<AlertTitle data-testid="undelegate-success">Undelegation complete</AlertTitle>
|
||||
{message}
|
||||
</Alert>
|
||||
}
|
||||
@@ -109,12 +98,14 @@ export const Undelegate = () => {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-end',
|
||||
borderTop: '1px solid grey[200]',
|
||||
bgcolor: 'grey[50]',
|
||||
bgcolor: 'grey.100',
|
||||
p: 2,
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
data-testid="finish-button"
|
||||
variant="contained"
|
||||
disableElevation
|
||||
onClick={() => {
|
||||
setStatus(EnumRequestStatus.initial)
|
||||
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 */
|
||||
import {
|
||||
Theme,
|
||||
ThemeOptions,
|
||||
Palette,
|
||||
PaletteOptions,
|
||||
} from '@mui/material/styles'
|
||||
import { Theme, ThemeOptions, Palette, PaletteOptions } from '@mui/material/styles'
|
||||
import { PaletteMode } from '@mui/material'
|
||||
|
||||
/**
|
||||
@@ -34,9 +29,12 @@ declare module '@mui/material/styles' {
|
||||
*/
|
||||
interface NymPalette {
|
||||
highlight: string
|
||||
success: string
|
||||
info: string
|
||||
background: { light: string; dark: string }
|
||||
text: {
|
||||
light: string
|
||||
dark: string
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,8 +63,7 @@ declare module '@mui/material/styles' {
|
||||
nym: NymPalette & NymWalletPalette
|
||||
}
|
||||
|
||||
type NymPaletteAndNymWalletPaletteOptions =
|
||||
Partial<NymPaletteAndNymWalletPalette>
|
||||
type NymPaletteAndNymWalletPaletteOptions = Partial<NymPaletteAndNymWalletPalette>
|
||||
|
||||
/**
|
||||
* Add anything not palette related to the theme here
|
||||
|
||||
@@ -21,9 +21,12 @@ import {
|
||||
const nymPalette: NymPalette = {
|
||||
/** emphasises important elements */
|
||||
highlight: '#FB6E4E',
|
||||
success: '#21D073',
|
||||
info: '#967FF0',
|
||||
background: { light: '#F4F6F8', dark: '#121726' },
|
||||
text: {
|
||||
light: '#F2F2F2',
|
||||
dark: '#121726',
|
||||
},
|
||||
}
|
||||
|
||||
@@ -48,7 +51,7 @@ const lightMode: NymPaletteVariant = {
|
||||
paper: '#FFFFFF',
|
||||
},
|
||||
text: {
|
||||
main: '#666666',
|
||||
main: '#121726',
|
||||
},
|
||||
topNav: {
|
||||
background: '#111826',
|
||||
@@ -80,6 +83,9 @@ const variantToMUIPalette = (variant: NymPaletteVariant): PaletteOptions => ({
|
||||
main: nymPalette.highlight,
|
||||
contrastText: '#fff',
|
||||
},
|
||||
success: {
|
||||
main: nymPalette.success,
|
||||
},
|
||||
background: {
|
||||
default: variant.background.main,
|
||||
paper: variant.background.paper,
|
||||
@@ -145,9 +151,7 @@ export const getDesignTokens = (mode: PaletteMode): ThemeOptions => {
|
||||
const { palette } = createTheme({
|
||||
palette: {
|
||||
mode,
|
||||
...(mode === 'light'
|
||||
? createLightModePalette()
|
||||
: createDarkModePalette()),
|
||||
...(mode === 'light' ? 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,
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ module.exports = {
|
||||
receivePageHeaderText: "Receive Nym",
|
||||
|
||||
//sendPage
|
||||
sendPunk: "Send PUNK",
|
||||
sendPunk: "Send punk",
|
||||
|
||||
//homePage
|
||||
homePageErrorMnemonic: "Error parsing bip39 mnemonic",
|
||||
|
||||
@@ -12,7 +12,7 @@ class WalletSend {
|
||||
return $("[data-testid='button");
|
||||
}
|
||||
get sendHeader() {
|
||||
return $("[data-testid='Send PUNK']");
|
||||
return $("[data-testid='Send punk']");
|
||||
}
|
||||
get accountBalance() {
|
||||
return $("[data-testid='account-balance']");
|
||||
|
||||
@@ -33,13 +33,18 @@ module.exports = {
|
||||
use: ['style-loader', 'css-loader'],
|
||||
},
|
||||
{
|
||||
test: /\.(png|jpe?g|gif|svg)$/i,
|
||||
test: /\.(png|jpe?g|gif)$/i,
|
||||
use: [
|
||||
{
|
||||
loader: 'file-loader',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.svg$/i,
|
||||
issuer: /\.[jt]sx?$/,
|
||||
use: ['@svgr/webpack'],
|
||||
},
|
||||
],
|
||||
},
|
||||
resolve: {
|
||||
|
||||
+1426
-38
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user