diff --git a/web/package.json b/web/package.json index 33cebe9e5..ef2fc4465 100644 --- a/web/package.json +++ b/web/package.json @@ -43,11 +43,11 @@ "@eversdk/appkit": "^0.3.8", "@eversdk/core": "^1.44.2", "@eversdk/lib-web": "^1.44.2", - "@fortawesome/fontawesome-svg-core": "^6.1.1", - "@fortawesome/free-brands-svg-icons": "^6.1.1", - "@fortawesome/free-regular-svg-icons": "^6.1.1", - "@fortawesome/free-solid-svg-icons": "^6.1.1", - "@fortawesome/react-fontawesome": "^0.1.18", + "@fortawesome/fontawesome-svg-core": "^6.4.2", + "@fortawesome/free-brands-svg-icons": "^6.4.2", + "@fortawesome/free-regular-svg-icons": "^6.4.2", + "@fortawesome/free-solid-svg-icons": "^6.4.2", + "@fortawesome/react-fontawesome": "^0.1.19", "@headlessui/react": "^1.6.0", "@monaco-editor/react": "^4.4.6", "@mui/icons-material": "^5.8.4", @@ -58,7 +58,7 @@ "@wysimark/react": "^2.2.15", "apexcharts": "^3.37.0", "buffer": "^6.0.3", - "classnames": "^2.3.1", + "classnames": "^2.3.2", "copy-to-clipboard": "^3.3.3", "crypto-js": "^4.1.1", "diff": "^5.1.0", diff --git a/web/src/utils.ts b/web/src/utils.ts index 9d1f73eff..33d8f1b5e 100644 --- a/web/src/utils.ts +++ b/web/src/utils.ts @@ -20,10 +20,40 @@ export const roundNumber = (value: number | string, precision: number = 5) => { return Math.round(floatvalue * multiplier) / multiplier } -export const getDurationDelta = (time: number) => { +export const getDurationDelta = (time: number, format: string) => { const ms = moment(time).diff(moment()) const delta = moment.duration(ms) - return `${delta.days()}d ${delta.hours()}h ${delta.minutes()}m` + + const parsed = [] + while (format) { + const sindex = format.indexOf('[') + const eindex = format.indexOf(']') + const group = format.slice(sindex + 1, eindex) + format = format.slice(eindex + 1) + + const nindex = format.indexOf('[') + const delimiter = format.slice(0, nindex) + format = format.slice(nindex) + + parsed.push([group, delimiter]) + } + + const filled = parsed.map(([group, delimiter]) => { + const [sign, label] = group.split(':').concat('') + + let value = 0 + if (sign === 'd') { + value = delta.days() + } else if (sign === 'h') { + value = delta.hours() + } else if (sign === 'm') { + value = delta.minutes() + } else if (sign === 's') { + value = delta.seconds() + } + return `${value}${label}${delimiter}` + }) + return filled.join('') } export const sleep = (ms: number = 0) => { @@ -134,6 +164,10 @@ export const fromBigint = (number: bigint, decimals: number) => { // 001234 -> '001234' const fraction = zeroPaddedValue.slice(-decimals).replace(/\.?0+$/, '') + if (integer === '' && fraction === '') { + return '0' + } + if (integer === '') { return `0.${fraction}` } diff --git a/web/src/v1.0.0/components/DaoEvent/Progress.tsx b/web/src/v1.0.0/components/DaoEvent/Progress.tsx index d676f35fa..45cbcd0f1 100644 --- a/web/src/v1.0.0/components/DaoEvent/Progress.tsx +++ b/web/src/v1.0.0/components/DaoEvent/Progress.tsx @@ -62,7 +62,7 @@ const DaoEventProgressBar = (props: TEventProgressBarProps) => { ? `Closed at ${new Date( time.completed || time.finish, ).toLocaleDateString()}` - : `Ends in ${getDurationDelta(time.finish)}`} + : `Ends in ${getDurationDelta(time.finish, '[d:d] [h:h] [m:m]')}`} ) diff --git a/web/src/v1.0.0/pages/DaoEvent/DaoEvent.tsx b/web/src/v1.0.0/pages/DaoEvent/DaoEvent.tsx index ada5199db..a5ed43170 100644 --- a/web/src/v1.0.0/pages/DaoEvent/DaoEvent.tsx +++ b/web/src/v1.0.0/pages/DaoEvent/DaoEvent.tsx @@ -128,7 +128,7 @@ const DaoEventPageInner = (props: { address: string }) => { End date
- {getDurationDelta(event.time.finish)} + {getDurationDelta(event.time.finish, '[d:d] [h:h] [m:m]')}
diff --git a/web/src/v1.0.0/pages/DaoEventList/components/ListItem.tsx b/web/src/v1.0.0/pages/DaoEventList/components/ListItem.tsx index 621b78cf7..a3e0510e5 100644 --- a/web/src/v1.0.0/pages/DaoEventList/components/ListItem.tsx +++ b/web/src/v1.0.0/pages/DaoEventList/components/ListItem.tsx @@ -99,7 +99,7 @@ const ListItem = (props: TListItemProps) => { {event.status.completed ? 'Completed' : event.time.finish > 0 - ? getDurationDelta(event.time.finish) + ? getDurationDelta(event.time.finish, '[d:d] [h:h] [m:m]') : '-'} diff --git a/web/src/v2.0.0/components/DaoEvent/Progress.tsx b/web/src/v2.0.0/components/DaoEvent/Progress.tsx index 5d8cd0661..7beeadb0c 100644 --- a/web/src/v2.0.0/components/DaoEvent/Progress.tsx +++ b/web/src/v2.0.0/components/DaoEvent/Progress.tsx @@ -68,7 +68,7 @@ const DaoEventProgressBar = (props: TEventProgressBarProps) => { time.completed || time.finish, ).toLocaleDateString()}` : time.finish > 0 - ? `Ends in ${getDurationDelta(time.finish)}` + ? `Ends in ${getDurationDelta(time.finish, '[d:d] [h:h] [m:m]')}` : 'Review required'} diff --git a/web/src/v2.0.0/pages/DaoEvent/DaoEvent.tsx b/web/src/v2.0.0/pages/DaoEvent/DaoEvent.tsx index b02afe0f1..d3993e1b1 100644 --- a/web/src/v2.0.0/pages/DaoEvent/DaoEvent.tsx +++ b/web/src/v2.0.0/pages/DaoEvent/DaoEvent.tsx @@ -151,7 +151,10 @@ const DaoEventPageInner = (props: { address: string }) => { event.time.completed || event.time.finish, ).toLocaleDateString() : event.time.finish > 0 - ? getDurationDelta(event.time.finish) + ? getDurationDelta( + event.time.finish, + '[d:d] [h:h] [m:m]', + ) : 'Review required'} diff --git a/web/src/v2.0.0/pages/DaoEventList/components/ListItem.tsx b/web/src/v2.0.0/pages/DaoEventList/components/ListItem.tsx index 71c8f1bd7..331ee13f3 100644 --- a/web/src/v2.0.0/pages/DaoEventList/components/ListItem.tsx +++ b/web/src/v2.0.0/pages/DaoEventList/components/ListItem.tsx @@ -115,7 +115,7 @@ const ListItem = (props: TListItemProps) => { {event.status.completed ? 'Completed' : event.time.finish > 0 - ? getDurationDelta(event.time.finish) + ? getDurationDelta(event.time.finish, '[d:d] [h:h] [m:m]') : '-'} diff --git a/web/src/v3.0.0/components/DaoEvent/Progress.tsx b/web/src/v3.0.0/components/DaoEvent/Progress.tsx index 5d8cd0661..7beeadb0c 100644 --- a/web/src/v3.0.0/components/DaoEvent/Progress.tsx +++ b/web/src/v3.0.0/components/DaoEvent/Progress.tsx @@ -68,7 +68,7 @@ const DaoEventProgressBar = (props: TEventProgressBarProps) => { time.completed || time.finish, ).toLocaleDateString()}` : time.finish > 0 - ? `Ends in ${getDurationDelta(time.finish)}` + ? `Ends in ${getDurationDelta(time.finish, '[d:d] [h:h] [m:m]')}` : 'Review required'} diff --git a/web/src/v3.0.0/pages/DaoEvent/DaoEvent.tsx b/web/src/v3.0.0/pages/DaoEvent/DaoEvent.tsx index d065d7d41..b67376939 100644 --- a/web/src/v3.0.0/pages/DaoEvent/DaoEvent.tsx +++ b/web/src/v3.0.0/pages/DaoEvent/DaoEvent.tsx @@ -154,7 +154,10 @@ const DaoEventPageInner = (props: { address: string }) => { event.time.completed || event.time.finish, ).toLocaleDateString() : event.time.finish > 0 - ? getDurationDelta(event.time.finish) + ? getDurationDelta( + event.time.finish, + '[d:d] [h:h] [m:m]', + ) : 'Review required'} diff --git a/web/src/v3.0.0/pages/DaoEventList/components/ListItem.tsx b/web/src/v3.0.0/pages/DaoEventList/components/ListItem.tsx index 71c8f1bd7..331ee13f3 100644 --- a/web/src/v3.0.0/pages/DaoEventList/components/ListItem.tsx +++ b/web/src/v3.0.0/pages/DaoEventList/components/ListItem.tsx @@ -115,7 +115,7 @@ const ListItem = (props: TListItemProps) => { {event.status.completed ? 'Completed' : event.time.finish > 0 - ? getDurationDelta(event.time.finish) + ? getDurationDelta(event.time.finish, '[d:d] [h:h] [m:m]') : '-'} diff --git a/web/src/v4.0.0/components/DaoEvent/Progress.tsx b/web/src/v4.0.0/components/DaoEvent/Progress.tsx index 5d8cd0661..7beeadb0c 100644 --- a/web/src/v4.0.0/components/DaoEvent/Progress.tsx +++ b/web/src/v4.0.0/components/DaoEvent/Progress.tsx @@ -68,7 +68,7 @@ const DaoEventProgressBar = (props: TEventProgressBarProps) => { time.completed || time.finish, ).toLocaleDateString()}` : time.finish > 0 - ? `Ends in ${getDurationDelta(time.finish)}` + ? `Ends in ${getDurationDelta(time.finish, '[d:d] [h:h] [m:m]')}` : 'Review required'} diff --git a/web/src/v4.0.0/pages/DaoEvent/DaoEvent.tsx b/web/src/v4.0.0/pages/DaoEvent/DaoEvent.tsx index cee09b0a2..da77b0d01 100644 --- a/web/src/v4.0.0/pages/DaoEvent/DaoEvent.tsx +++ b/web/src/v4.0.0/pages/DaoEvent/DaoEvent.tsx @@ -155,7 +155,10 @@ const DaoEventPageInner = (props: { address: string }) => { event.time.completed || event.time.finish, ).toLocaleDateString() : event.time.finish > 0 - ? getDurationDelta(event.time.finish) + ? getDurationDelta( + event.time.finish, + '[d:d] [h:h] [m:m]', + ) : 'Review required'} diff --git a/web/src/v4.0.0/pages/DaoEventList/components/ListItem.tsx b/web/src/v4.0.0/pages/DaoEventList/components/ListItem.tsx index 71c8f1bd7..331ee13f3 100644 --- a/web/src/v4.0.0/pages/DaoEventList/components/ListItem.tsx +++ b/web/src/v4.0.0/pages/DaoEventList/components/ListItem.tsx @@ -115,7 +115,7 @@ const ListItem = (props: TListItemProps) => { {event.status.completed ? 'Completed' : event.time.finish > 0 - ? getDurationDelta(event.time.finish) + ? getDurationDelta(event.time.finish, '[d:d] [h:h] [m:m]') : '-'} diff --git a/web/src/v5.0.0/components/DaoEvent/Progress.tsx b/web/src/v5.0.0/components/DaoEvent/Progress.tsx index 5d8cd0661..7beeadb0c 100644 --- a/web/src/v5.0.0/components/DaoEvent/Progress.tsx +++ b/web/src/v5.0.0/components/DaoEvent/Progress.tsx @@ -68,7 +68,7 @@ const DaoEventProgressBar = (props: TEventProgressBarProps) => { time.completed || time.finish, ).toLocaleDateString()}` : time.finish > 0 - ? `Ends in ${getDurationDelta(time.finish)}` + ? `Ends in ${getDurationDelta(time.finish, '[d:d] [h:h] [m:m]')}` : 'Review required'} diff --git a/web/src/v5.0.0/pages/DaoEvent/DaoEvent.tsx b/web/src/v5.0.0/pages/DaoEvent/DaoEvent.tsx index cee09b0a2..da77b0d01 100644 --- a/web/src/v5.0.0/pages/DaoEvent/DaoEvent.tsx +++ b/web/src/v5.0.0/pages/DaoEvent/DaoEvent.tsx @@ -155,7 +155,10 @@ const DaoEventPageInner = (props: { address: string }) => { event.time.completed || event.time.finish, ).toLocaleDateString() : event.time.finish > 0 - ? getDurationDelta(event.time.finish) + ? getDurationDelta( + event.time.finish, + '[d:d] [h:h] [m:m]', + ) : 'Review required'} diff --git a/web/src/v5.0.0/pages/DaoEventList/components/ListItem.tsx b/web/src/v5.0.0/pages/DaoEventList/components/ListItem.tsx index 71c8f1bd7..331ee13f3 100644 --- a/web/src/v5.0.0/pages/DaoEventList/components/ListItem.tsx +++ b/web/src/v5.0.0/pages/DaoEventList/components/ListItem.tsx @@ -115,7 +115,7 @@ const ListItem = (props: TListItemProps) => { {event.status.completed ? 'Completed' : event.time.finish > 0 - ? getDurationDelta(event.time.finish) + ? getDurationDelta(event.time.finish, '[d:d] [h:h] [m:m]') : '-'} diff --git a/web/src/v5.1.0/components/DaoEvent/Progress.tsx b/web/src/v5.1.0/components/DaoEvent/Progress.tsx index 5d8cd0661..7beeadb0c 100644 --- a/web/src/v5.1.0/components/DaoEvent/Progress.tsx +++ b/web/src/v5.1.0/components/DaoEvent/Progress.tsx @@ -68,7 +68,7 @@ const DaoEventProgressBar = (props: TEventProgressBarProps) => { time.completed || time.finish, ).toLocaleDateString()}` : time.finish > 0 - ? `Ends in ${getDurationDelta(time.finish)}` + ? `Ends in ${getDurationDelta(time.finish, '[d:d] [h:h] [m:m]')}` : 'Review required'} diff --git a/web/src/v5.1.0/pages/DaoEvent/DaoEvent.tsx b/web/src/v5.1.0/pages/DaoEvent/DaoEvent.tsx index cee09b0a2..da77b0d01 100644 --- a/web/src/v5.1.0/pages/DaoEvent/DaoEvent.tsx +++ b/web/src/v5.1.0/pages/DaoEvent/DaoEvent.tsx @@ -155,7 +155,10 @@ const DaoEventPageInner = (props: { address: string }) => { event.time.completed || event.time.finish, ).toLocaleDateString() : event.time.finish > 0 - ? getDurationDelta(event.time.finish) + ? getDurationDelta( + event.time.finish, + '[d:d] [h:h] [m:m]', + ) : 'Review required'} diff --git a/web/src/v5.1.0/pages/DaoEventList/components/ListItem.tsx b/web/src/v5.1.0/pages/DaoEventList/components/ListItem.tsx index 71c8f1bd7..331ee13f3 100644 --- a/web/src/v5.1.0/pages/DaoEventList/components/ListItem.tsx +++ b/web/src/v5.1.0/pages/DaoEventList/components/ListItem.tsx @@ -115,7 +115,7 @@ const ListItem = (props: TListItemProps) => { {event.status.completed ? 'Completed' : event.time.finish > 0 - ? getDurationDelta(event.time.finish) + ? getDurationDelta(event.time.finish, '[d:d] [h:h] [m:m]') : '-'} diff --git a/web/src/v6.0.0/components/DaoEvent/Progress.tsx b/web/src/v6.0.0/components/DaoEvent/Progress.tsx index 5d8cd0661..7beeadb0c 100644 --- a/web/src/v6.0.0/components/DaoEvent/Progress.tsx +++ b/web/src/v6.0.0/components/DaoEvent/Progress.tsx @@ -68,7 +68,7 @@ const DaoEventProgressBar = (props: TEventProgressBarProps) => { time.completed || time.finish, ).toLocaleDateString()}` : time.finish > 0 - ? `Ends in ${getDurationDelta(time.finish)}` + ? `Ends in ${getDurationDelta(time.finish, '[d:d] [h:h] [m:m]')}` : 'Review required'} diff --git a/web/src/v6.0.0/pages/DaoEvent/DaoEvent.tsx b/web/src/v6.0.0/pages/DaoEvent/DaoEvent.tsx index cee09b0a2..da77b0d01 100644 --- a/web/src/v6.0.0/pages/DaoEvent/DaoEvent.tsx +++ b/web/src/v6.0.0/pages/DaoEvent/DaoEvent.tsx @@ -155,7 +155,10 @@ const DaoEventPageInner = (props: { address: string }) => { event.time.completed || event.time.finish, ).toLocaleDateString() : event.time.finish > 0 - ? getDurationDelta(event.time.finish) + ? getDurationDelta( + event.time.finish, + '[d:d] [h:h] [m:m]', + ) : 'Review required'} diff --git a/web/src/v6.0.0/pages/DaoEventList/components/ListItem.tsx b/web/src/v6.0.0/pages/DaoEventList/components/ListItem.tsx index 71c8f1bd7..331ee13f3 100644 --- a/web/src/v6.0.0/pages/DaoEventList/components/ListItem.tsx +++ b/web/src/v6.0.0/pages/DaoEventList/components/ListItem.tsx @@ -115,7 +115,7 @@ const ListItem = (props: TListItemProps) => { {event.status.completed ? 'Completed' : event.time.finish > 0 - ? getDurationDelta(event.time.finish) + ? getDurationDelta(event.time.finish, '[d:d] [h:h] [m:m]') : '-'} diff --git a/web/src/v6.1.0/components/DaoEvent/Progress.tsx b/web/src/v6.1.0/components/DaoEvent/Progress.tsx index 5d8cd0661..7beeadb0c 100644 --- a/web/src/v6.1.0/components/DaoEvent/Progress.tsx +++ b/web/src/v6.1.0/components/DaoEvent/Progress.tsx @@ -68,7 +68,7 @@ const DaoEventProgressBar = (props: TEventProgressBarProps) => { time.completed || time.finish, ).toLocaleDateString()}` : time.finish > 0 - ? `Ends in ${getDurationDelta(time.finish)}` + ? `Ends in ${getDurationDelta(time.finish, '[d:d] [h:h] [m:m]')}` : 'Review required'} diff --git a/web/src/v6.1.0/hooks/l2.hooks.ts b/web/src/v6.1.0/hooks/l2.hooks.ts index c5e8020d7..d536f3f76 100644 --- a/web/src/v6.1.0/hooks/l2.hooks.ts +++ b/web/src/v6.1.0/hooks/l2.hooks.ts @@ -7,9 +7,10 @@ import { AppConfig } from '../../appconfig' import { appToastStatusSelector } from '../../store/app.state' import { GoshError } from '../../errors' import { useUser } from './user.hooks' -import { fromBigint, toBigint, whileFinite } from '../../utils' +import { fromBigint, setLockableInterval, toBigint, whileFinite } from '../../utils' import { EL2Network, TL2TransferStatusItem, TL2User } from '../types/l2.types' import { L2_COMISSION } from '../../constants' +import { supabase } from '../../supabase' export function useL2Transfer(options: { initialize?: boolean } = {}) { const { initialize } = options @@ -162,29 +163,30 @@ export function useL2Transfer(options: { initialize?: boolean } = {}) { }, } }) - } else if (values.from_amount && parseFloat(values.from_amount) > 0) { + } else if (values.from_amount && parseFloat(values.from_amount) >= 0) { setData((state) => { - // Subtract comission when ETH -> GOSH - let to_amount = toBigint( + // Cast from_amount to BigInt + const from_amount = toBigint( values.from_amount!, state.networks[state.summary.from.network].decimals, ) - if (state.summary.from.network === EL2Network.ETH) { - to_amount = to_amount - to_amount / BigInt(L2_COMISSION) + + // Calculate comission + const route = `${data.summary.from.network}:${data.summary.to.network}` + let comission = 0n + if (route === `${EL2Network.ETH}:${EL2Network.GOSH}`) { + comission = from_amount / BigInt(L2_COMISSION) + } else if (route === `${EL2Network.GOSH}:${EL2Network.ETH}`) { + comission = data.comissions[route] } + // to.amount is calculated by useCallback with deps return { ...state, + comissions: { ...state.comissions, [route]: comission }, summary: { ...state.summary, from: { ...state.summary.from, amount: values.from_amount! }, - to: { - ...state.summary.to, - amount: fromBigint( - to_amount, - state.networks[state.summary.to.network].decimals, - ), - }, }, } }) @@ -568,6 +570,36 @@ export function useL2Transfer(options: { initialize?: boolean } = {}) { })) }, [data.gosh.instance?.address]) + const getWeb3Comission = useCallback(async () => { + try { + const { data, error } = await supabase.client + .from('l2_state') + .select() + .order('created_at', { ascending: false }) + if (error) { + throw new GoshError('Get web3 comission', error.message) + } + if (!data.length) { + throw new GoshError('Get web3 comission', 'No data') + } + + const row = data[0] + let comission = BigInt(row.current_approximate_elock_commissions) + comission += 21000n * BigInt(row.current_eth_gas_price) + comission /= BigInt(row.queued_burns_cnt + 1) + + setData((state) => ({ + ...state, + comissions: { + ...state.comissions, + [`${EL2Network.GOSH}:${EL2Network.ETH}`]: comission, + }, + })) + } catch (e: any) { + console.warn(e.message) + } + }, []) + // Connect GOSH account useEffect(() => { if (initialize) { @@ -609,7 +641,7 @@ export function useL2Transfer(options: { initialize?: boolean } = {}) { } }, [initialize, goshSubscribeCallback]) - // Subscribe ETH account/provider + // Subscribe web3 account/provider useEffect(() => { if (!initialize) { return @@ -631,6 +663,51 @@ export function useL2Transfer(options: { initialize?: boolean } = {}) { } }, [initialize, web3SubscribeCallback]) + // Subscribe (periodic update) for web3 comission + useEffect(() => { + let interval: NodeJS.Timer + if (initialize) { + getWeb3Comission() + interval = setLockableInterval(async () => { + await getWeb3Comission() + }, 15000) + } + + return () => { + clearInterval(interval) + } + }, [initialize, getWeb3Comission]) + + // Update to_amount by deps + useEffect(() => { + const { summary, comissions, networks } = data + const comission = comissions[`${summary.from.network}:${summary.to.network}`] + const from_amount = toBigint( + summary.from.amount, + networks[summary.from.network].decimals, + ) + const to_amount = from_amount - comission + + setData((state) => ({ + ...state, + summary: { + ...state.summary, + to: { + ...state.summary.to, + amount: fromBigint( + to_amount > 0 ? to_amount : 0n, + networks[state.summary.to.network].decimals, + ), + }, + }, + })) + }, [ + data.summary.from.network, + data.summary.to.network, + data.summary.from.amount, + data.comissions, + ]) + return { ...data, status, diff --git a/web/src/v6.1.0/pages/DaoEvent/DaoEvent.tsx b/web/src/v6.1.0/pages/DaoEvent/DaoEvent.tsx index 3cd13dcf5..f21e25461 100644 --- a/web/src/v6.1.0/pages/DaoEvent/DaoEvent.tsx +++ b/web/src/v6.1.0/pages/DaoEvent/DaoEvent.tsx @@ -159,7 +159,10 @@ const DaoEventPageInner = (props: { address: string }) => { event.time.completed || event.time.finish, ).toLocaleDateString() : event.time.finish > 0 - ? getDurationDelta(event.time.finish) + ? getDurationDelta( + event.time.finish, + '[d:d] [h:h] [m:m]', + ) : 'Review required'} diff --git a/web/src/v6.1.0/pages/DaoEventList/components/ListItem.tsx b/web/src/v6.1.0/pages/DaoEventList/components/ListItem.tsx index 71c8f1bd7..331ee13f3 100644 --- a/web/src/v6.1.0/pages/DaoEventList/components/ListItem.tsx +++ b/web/src/v6.1.0/pages/DaoEventList/components/ListItem.tsx @@ -115,7 +115,7 @@ const ListItem = (props: TListItemProps) => { {event.status.completed ? 'Completed' : event.time.finish > 0 - ? getDurationDelta(event.time.finish) + ? getDurationDelta(event.time.finish, '[d:d] [h:h] [m:m]') : '-'} diff --git a/web/src/v6.1.0/pages/L2/L2.tsx b/web/src/v6.1.0/pages/L2/L2.tsx index cd7553f71..0ba615fa7 100644 --- a/web/src/v6.1.0/pages/L2/L2.tsx +++ b/web/src/v6.1.0/pages/L2/L2.tsx @@ -1,13 +1,14 @@ import { AnimatePresence, motion } from 'framer-motion' import { useL2Transfer } from '../../hooks/l2.hooks' import { CompleteStep, TransferStep, RouteStep, Breadcrumbs } from './components' -import { fromBigint, shortString, roundNumber } from '../../../utils' -import { useEffect } from 'react' +import { fromBigint, shortString, roundNumber, getDurationDelta } from '../../../utils' +import { useEffect, useState } from 'react' import Alert from '../../../components/Alert' import CopyClipboard from '../../../components/CopyClipboard' import { useErrorBoundary, withErrorBoundary } from 'react-error-boundary' import { EL2Network } from '../../types/l2.types' import { Button } from '../../../components/Form' +import moment from 'moment' const motionProps = { initial: { opacity: 0 }, @@ -16,12 +17,25 @@ const motionProps = { transition: { duration: 0.25 }, } +const getPayoutTime = () => { + const hours = [0, 3, 6, 9, 12, 15, 18, 21] + const moments = hours.map((h) => moment.utc({ hour: h, minute: 0, second: 0 })) + moments.push(moment.utc({ day: moment.utc().date() + 1, hour: 0, second: 0 })) + + const now = moment() + const diffs = moments.map((m) => m.diff(now, 'seconds')) + const index = diffs.findIndex((diff) => diff > 0) + return moments[index] +} + const L2PageInner = () => { const { showBoundary } = useErrorBoundary() - const { web3, gosh, networks, summary, step, reset, error, connectWeb3 } = + const { web3, gosh, comissions, networks, summary, step, reset, error, connectWeb3 } = useL2Transfer({ initialize: true, }) + const [payout, setPayout] = useState(getPayoutTime()) + const route = `${summary.from.network}:${summary.to.network}` const getNetworkBalance = (network: string) => { const floatstr = fromBigint(networks[network].balance, networks[network].decimals) @@ -35,7 +49,10 @@ const L2PageInner = () => { }, [error]) useEffect(() => { + const interval = setInterval(() => setPayout(getPayoutTime()), 1000) + return () => { + clearInterval(interval) reset() } }, []) @@ -196,6 +213,30 @@ const L2PageInner = () => { + +
+
+
Estimate comission
+
+ {fromBigint( + comissions[route], + networks[summary.to.network].decimals, + )}{' '} + + {networks[summary.to.network].token} + +
+
+ + {route === `${EL2Network.GOSH}:${EL2Network.ETH}` && ( +
+
Next payout
+
+ {getDurationDelta(payout, '[h:h] [m:m] [s:s]')} +
+
+ )} +
diff --git a/web/src/v6.1.0/pages/L2/components/RouteStep/AmountField.tsx b/web/src/v6.1.0/pages/L2/components/RouteStep/AmountField.tsx index 741c007b5..cb0b38590 100644 --- a/web/src/v6.1.0/pages/L2/components/RouteStep/AmountField.tsx +++ b/web/src/v6.1.0/pages/L2/components/RouteStep/AmountField.tsx @@ -4,6 +4,7 @@ import { useL2Transfer } from '../../../../hooks/l2.hooks' import { Button } from '../../../../../components/Form' import classNames from 'classnames' import { fromBigint, roundNumber } from '../../../../../utils' +import { useRef } from 'react' type TAmountFieldProps = { network: string @@ -15,7 +16,8 @@ type TAmountFieldProps = { const AmountField = (props: TAmountFieldProps) => { const { network, prefix, label, disabled, onChange } = props - const { networks, setSummaryFormValues } = useL2Transfer() + const ref = useRef(null) + const { networks } = useL2Transfer() const getNetworkBalance = (network: string) => { const floatstr = fromBigint(networks[network].balance, networks[network].decimals) @@ -23,12 +25,15 @@ const AmountField = (props: TAmountFieldProps) => { } const onMaxClick = () => { - setSummaryFormValues({ - from_amount: fromBigint( - networks[network].balance, - networks[network].decimals, - ), - }) + if (!ref.current) { + return + } + + ref.current.value = fromBigint( + networks[network].balance, + networks[network].decimals, + ) + onChange && onChange({ target: ref.current }) } return ( @@ -41,6 +46,7 @@ const AmountField = (props: TAmountFieldProps) => { readOnly={disabled} disabled={disabled} inputProps={{ + ref: ref, after: (
{networks[network].token} diff --git a/web/src/v6.1.0/pages/L2/components/RouteStep/RouteStep.tsx b/web/src/v6.1.0/pages/L2/components/RouteStep/RouteStep.tsx index ac9c5c856..ae19f7f0b 100644 --- a/web/src/v6.1.0/pages/L2/components/RouteStep/RouteStep.tsx +++ b/web/src/v6.1.0/pages/L2/components/RouteStep/RouteStep.tsx @@ -82,11 +82,11 @@ const RouteStep = () => { from_network: summary.from.network, from_user: summary.from.user, from_wallet: summary.from.wallet, - from_amount: summary.from.amount.toString(), + from_amount: summary.from.amount, to_network: summary.to.network, to_user: summary.to.user, to_wallet: summary.to.wallet, - to_amount: summary.to.amount.toString(), + to_amount: summary.to.amount, }} validationSchema={yup.object().shape({ from_network: yup.string().required(), diff --git a/web/src/v6.1.0/store/l2.state.ts b/web/src/v6.1.0/store/l2.state.ts index 2ba9c52f5..9972b54f9 100644 --- a/web/src/v6.1.0/store/l2.state.ts +++ b/web/src/v6.1.0/store/l2.state.ts @@ -13,6 +13,11 @@ export const l2TransferAtom = atom({ instance: null, address: '', }, + comissions: { + [`${EL2Network.ETH}:${EL2Network.GOSH}`]: 0n, + [`${EL2Network.GOSH}:${EL2Network.ETH}`]: 0n, + [`${EL2Network.GOSH}:${EL2Network.GOSH}`]: 0n, + }, networks: { [EL2Network.ETH]: { label: 'Ethereum', diff --git a/web/src/v6.1.0/types/l2.types.ts b/web/src/v6.1.0/types/l2.types.ts index 5b913f247..41310e52c 100644 --- a/web/src/v6.1.0/types/l2.types.ts +++ b/web/src/v6.1.0/types/l2.types.ts @@ -26,6 +26,7 @@ export type TL2TransferData = { instance: TIP3Wallet | null address: string } + comissions: { [route: string]: bigint } networks: { [key: string]: { label: string