Compare commits

...

21 Commits

Author SHA1 Message Date
Bogdan-Ștefan Neacșu 233b4590e7 Add custom denom balance query 2021-12-13 17:43:46 +02:00
fmtabbara 3d973a8eb7 link to transaction hash 2021-12-13 17:43:46 +02:00
fmtabbara 484f422eff style update 2021-12-13 17:43:46 +02:00
fmtabbara d90fe72785 style updates 2021-12-13 17:43:46 +02:00
fmtabbara d15f9d9e07 refresh balance on balance page visit 2021-12-13 17:43:46 +02:00
fmtabbara f184bdfb4b update bonding success UI 2021-12-13 17:43:46 +02:00
fmtabbara 3c9988365a undelegate page update 2021-12-13 17:43:46 +02:00
fmtabbara 38b49940d7 update delegate page 2021-12-13 17:43:46 +02:00
fmtabbara 41b5ffa872 update bond page 2021-12-13 17:43:46 +02:00
fmtabbara ca3f6bb511 update send page 2021-12-13 17:43:46 +02:00
fmtabbara e380da4ba2 undo hard-coded data 2021-12-13 17:43:46 +02:00
fmtabbara 4d2974e383 add custom nav icons 2021-12-13 17:43:46 +02:00
fmtabbara 88bde805f0 use svgs as React components with svgr 2021-12-13 17:43:46 +02:00
fmtabbara b562137d23 balance page updates 2021-12-13 17:43:46 +02:00
fmtabbara dc4a84cda9 fix memory leak 2021-12-13 17:43:46 +02:00
Bogdan-Ștefan Neacșu 87536b9f48 Update vesting contract address on nova 2021-12-13 14:51:36 +02:00
Bogdan-Ștefan Neacșu 3a9d1b2503 Uploaded erc20 bridge contract and update its address on nova 2021-12-13 13:41:03 +02:00
Bogdan-Ștefan Neacșu 7616104abb Use the same address for vesting contract until we upload it 2021-12-10 18:11:54 +02:00
Bogdan-Ștefan Neacșu 152b8dd320 Update rewarding validator address 2021-12-10 17:38:30 +02:00
Bogdan-Ștefan Neacșu 5c6680209f Update network defaults 2021-12-10 16:35:09 +02:00
Bogdan-Ștefan Neacșu bb17c2ddfa Short node identity signature check 2021-12-10 16:35:09 +02:00
44 changed files with 1914 additions and 565 deletions
@@ -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,
+10 -13
View File
@@ -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;
+3
View File
@@ -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];
+1
View File
@@ -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>
)}
</>
)
}
+8 -19
View File
@@ -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}
/> />
+4 -11
View File
@@ -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}
+8 -10
View File
@@ -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>
)
}
+2 -1
View File
@@ -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'
+2 -7
View File
@@ -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)
+3 -3
View File
@@ -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 }
} }
+7
View File
@@ -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

+3 -2
View File
@@ -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 />
+14 -7
View File
@@ -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>
+21 -38
View File
@@ -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>
)}
</>
)
}
+11 -17
View File
@@ -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,
}} }}
> >
+13 -29
View File
@@ -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>
)}
</>
)
}
+12 -28
View File
@@ -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,
}} }}
> >
+30 -52
View File
@@ -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>
+12 -15
View File
@@ -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>
) )
} }
+32 -62
View File
@@ -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>
</> </>
+27 -41
View File
@@ -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>
+1 -1
View File
@@ -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>
+2 -4
View File
@@ -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)} />
)} )}
+25 -47
View File
@@ -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,
}} }}
> >
+7 -16
View File
@@ -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()
+24
View File
@@ -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>
)
}
+14
View File
@@ -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>
)
}
+4
View File
@@ -0,0 +1,4 @@
export * from './delegate'
export * from './undelegate'
export * from './bond'
export * from './unbond'
+19
View File
@@ -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>
)
}
+15
View File
@@ -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>
)
}
+5 -8
View File
@@ -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
+20 -4
View File
@@ -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']");
+6 -1
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff