From 2ebc5d07c9e2d9e50a385b7983dcbcbc95bbfa25 Mon Sep 17 00:00:00 2001 From: Gonza Montiel Date: Wed, 14 Feb 2024 15:14:12 -0300 Subject: [PATCH 1/7] feat: Update designs for Updating and Claiming staking rewards (#336) * staking rewards design changes * use our Amount component for claiming rewards * color tweaks for amplitude --- src/pages/collators/CollatorRewards.tsx | 86 ++++++++------ src/pages/collators/Collators.tsx | 2 +- .../collators/dialogs/ClaimRewardsDialog.tsx | 111 ++++++++++-------- .../collators/dialogs/ValidationSchema.ts | 11 ++ src/pages/collators/styles.css | 47 ++++++-- 5 files changed, 166 insertions(+), 91 deletions(-) diff --git a/src/pages/collators/CollatorRewards.tsx b/src/pages/collators/CollatorRewards.tsx index 5b5c0657..ea4056d3 100644 --- a/src/pages/collators/CollatorRewards.tsx +++ b/src/pages/collators/CollatorRewards.tsx @@ -1,3 +1,4 @@ +import { ExclamationCircleIcon } from '@heroicons/react/24/outline'; import { useCallback, useEffect, useMemo, useState } from 'preact/compat'; import { Button } from 'react-daisyui'; import { toast } from 'react-toastify'; @@ -12,6 +13,8 @@ import { nativeToFormat } from '../../shared/parseNumbers'; import { UserStaking } from './CollatorColumns'; import ClaimRewardsDialog from './dialogs/ClaimRewardsDialog'; +const WAIT_15_MINUTES = 15 * 60 * 1000; + function CollatorRewards() { const { api, tokenSymbol, ss58Format } = useNodeInfoState().state; const { walletAccount } = useGlobalState(); @@ -22,6 +25,7 @@ function CollatorRewards() { const [claimDialogOpen, setClaimDialogOpen] = useState(false); const [submissionPending, setSubmissionPending] = useState(false); const [unstaking, setUnstaking] = useState('0.00'); + const [updateEnabled, setUpdateEnabled] = useState(true); const userAccountAddress = useMemo(() => { return walletAccount && ss58Format ? getAddressForFormat(walletAccount?.address, ss58Format) : ''; @@ -68,7 +72,7 @@ function CollatorRewards() { return createUpdateDelegatorRewardsExtrinsic(); }, [api, createUpdateDelegatorRewardsExtrinsic]); - const submitUpdateExtrinsic = useCallback(() => { + const submitUpdate = useCallback(() => { if (!updateRewardsExtrinsic || !api || !walletAccount) { return; } @@ -89,6 +93,10 @@ function CollatorRewards() { refreshRewards(); if (errors.length === 0) { toast('Delegator rewards updated', { type: 'success' }); + setUpdateEnabled(false); + setTimeout(() => { + setUpdateEnabled(true); + }, WAIT_15_MINUTES); } } }) @@ -98,15 +106,15 @@ function CollatorRewards() { }); setSubmissionPending(false); }); - }, [api, refreshRewards, updateRewardsExtrinsic, walletAccount]); + }, [api, refreshRewards, updateRewardsExtrinsic, walletAccount, setUpdateEnabled]); return ( <>
-
-

Staking

-
+
+

Staking

+
@@ -118,47 +126,32 @@ function CollatorRewards() {

{nativeToFormat(userAvailableBalance, tokenSymbol)}

Free balance

-
- +
+
+

{nativeToFormat(unstaking, tokenSymbol)}

+
+ +
+
+
-
-

{nativeToFormat(parseInt(unstaking), tokenSymbol)}

- -
-
-

Staking Rewards

+
+

Staking Rewards

-

{nativeToFormat(estimatedRewards, tokenSymbol)}

+

{nativeToFormat(estimatedRewards, tokenSymbol)}

Estimated reward

-
- - +
+ {UpdateButton()} + {ClaimButton()}
@@ -172,6 +165,31 @@ function CollatorRewards() { /> ); + + function ClaimButton() { + return ( + + ); + } + + function UpdateButton() { + return ( + + ); + } } export default CollatorRewards; diff --git a/src/pages/collators/Collators.tsx b/src/pages/collators/Collators.tsx index 567f3731..180db5f7 100644 --- a/src/pages/collators/Collators.tsx +++ b/src/pages/collators/Collators.tsx @@ -4,7 +4,7 @@ import './styles.css'; function Collators() { return ( -
+
diff --git a/src/pages/collators/dialogs/ClaimRewardsDialog.tsx b/src/pages/collators/dialogs/ClaimRewardsDialog.tsx index 2998cf7d..01a8d6a5 100644 --- a/src/pages/collators/dialogs/ClaimRewardsDialog.tsx +++ b/src/pages/collators/dialogs/ClaimRewardsDialog.tsx @@ -1,13 +1,17 @@ +import { yupResolver } from '@hookform/resolvers/yup'; import { useCallback, useMemo, useState } from 'preact/hooks'; import { Button, Modal } from 'react-daisyui'; +import { useForm } from 'react-hook-form'; import { toast } from 'react-toastify'; import { useGlobalState } from '../../../GlobalStateProvider'; import { useNodeInfoState } from '../../../NodeInfoProvider'; import SuccessDialogIcon from '../../../assets/dialog-status-success'; import { CloseButton } from '../../../components/CloseButton'; +import Amount from '../../../components/Form/Amount'; import { getErrors } from '../../../helpers/substrate'; import { ParachainStakingInflationInflationInfo, useStakingPallet } from '../../../hooks/staking/staking'; -import { format, nativeToDecimal } from '../../../shared/parseNumbers'; +import { nativeToDecimal } from '../../../shared/parseNumbers'; +import { getClaimingValidationSchema } from './ValidationSchema'; interface Props { userRewardsBalance?: string; @@ -23,6 +27,10 @@ enum ClaimStep { Success = 1, } +export type ClaimFormValues = { + amount: number; +}; + function ClaimRewardsDialog(props: Props) { const { userRewardsBalance = '0', tokenSymbol, visible, onClose } = props; const { createClaimRewardExtrinsic } = useStakingPallet(); @@ -30,7 +38,16 @@ function ClaimRewardsDialog(props: Props) { const { walletAccount } = useGlobalState(); const [loading, setLoading] = useState(false); const [step, setStep] = useState(ClaimStep.Confirm); - const amount = nativeToDecimal(userRewardsBalance); + const balance = nativeToDecimal(userRewardsBalance).toNumber(); + + const form = useForm({ + resolver: yupResolver(getClaimingValidationSchema(balance)), + defaultValues: { + amount: parseFloat(balance.toFixed(2)), + }, + }); + + const { handleSubmit, formState, register, setValue, getValues, control } = form; useMemo(() => { if (!visible) @@ -39,46 +56,55 @@ function ClaimRewardsDialog(props: Props) { }, 500); }, [visible]); - const submitExtrinsic = useCallback(() => { - if (!walletAccount || !api || !amount) return; + const submitExtrinsic = useCallback( + (selectedAmount: number) => { + if (!walletAccount || !api || formState.errors.amount) return; - setLoading(true); + setLoading(true); - const extrinsic = createClaimRewardExtrinsic(userRewardsBalance); + const extrinsic = createClaimRewardExtrinsic(selectedAmount.toString()); - extrinsic - // eslint-disable-next-line @typescript-eslint/no-explicit-any - ?.signAndSend(walletAccount.address, { signer: walletAccount.signer as any }, (result) => { - const { status, events } = result; + extrinsic + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ?.signAndSend(walletAccount.address, { signer: walletAccount.signer as any }, (result) => { + const { status, events } = result; - const errors = getErrors(events, api); - if (status.isInBlock) { - if (errors.length > 0) { - const errorMessage = `Transaction failed with errors: ${errors.join('\n')}`; - console.error(errorMessage); - toast(errorMessage, { type: 'error' }); + const errors = getErrors(events, api); + if (status.isInBlock) { + if (errors.length > 0) { + const errorMessage = `Transaction failed with errors: ${errors.join('\n')}`; + console.error(errorMessage); + toast(errorMessage, { type: 'error' }); + } + } else if (status.isFinalized) { + setLoading(false); + if (errors.length === 0) { + setStep(ClaimStep.Success); + } } - } else if (status.isFinalized) { + }) + .catch((error) => { + console.error('Transaction submission failed', error); + toast('Transaction submission failed', { type: 'error' }); setLoading(false); - if (errors.length === 0) { - setStep(ClaimStep.Success); - } - } - }) - .catch((error) => { - console.error('Transaction submission failed', error); - toast('Transaction submission failed', { type: 'error' }); - setLoading(false); - }); - }, [api, amount, createClaimRewardExtrinsic, walletAccount, setLoading, setStep, userRewardsBalance]); + }); + }, + [api, formState, createClaimRewardExtrinsic, walletAccount, setLoading, setStep, userRewardsBalance], + ); const content = useMemo(() => { switch (step) { case ClaimStep.Confirm: return ( -
-

Amount

-

{format(amount.toNumber(), tokenSymbol)}

+
+
+ setValue('amount', n)} + error={formState.errors.amount?.message?.toString()} + /> +
); case ClaimStep.Success: @@ -90,7 +116,7 @@ function ClaimRewardsDialog(props: Props) {
); } - }, [amount, step, tokenSymbol]); + }, [step, tokenSymbol, formState]); const getButtonText = (step: ClaimStep) => { switch (step) { @@ -105,7 +131,7 @@ function ClaimRewardsDialog(props: Props) { switch (step) { case ClaimStep.Confirm: return () => { - submitExtrinsic(); + submitExtrinsic(getValues().amount); }; case ClaimStep.Success: return onClose; @@ -113,21 +139,12 @@ function ClaimRewardsDialog(props: Props) { }; return ( - - Claim Rewards + + Claim Rewards - -
- {content} - - - diff --git a/src/pages/collators/dialogs/ValidationSchema.ts b/src/pages/collators/dialogs/ValidationSchema.ts index 9c7d805f..058f08b2 100644 --- a/src/pages/collators/dialogs/ValidationSchema.ts +++ b/src/pages/collators/dialogs/ValidationSchema.ts @@ -15,3 +15,14 @@ export function getStakingValidationSchema(max: number) { .max(max, 'Amount is higher than available.'), }); } + +export function getClaimingValidationSchema(max: number) { + return Yup.object().shape({ + amount: Yup.number() + .typeError('Value is invalid.') + .transform(transformNumber) + .positive('You need to enter a positive number.') + .required('This field is required.') + .max(max, 'Amount is higher than available.'), + }); +} diff --git a/src/pages/collators/styles.css b/src/pages/collators/styles.css index a91dd730..da80c3dc 100644 --- a/src/pages/collators/styles.css +++ b/src/pages/collators/styles.css @@ -14,7 +14,21 @@ .collators-box { border: 1px solid #434343; } + + .collators-box .card-title { + color: #fff; + } + + .collators-box h3{ + color: #fff; + font-weight: 500; + } + .collators-box p{ + color: #fff; + font-weight: 300; + } + .staked-icon { fill: #fff; } @@ -29,17 +43,28 @@ } } -[data-theme='pendulum'] { - .btn-outline.btn-primary { - color: #252733; +[data-theme='pendulum'] { + .collators-box { + border: 1px solid #E5E5E5; } - - .btn-outline.btn-primary:hover { - color: #fff; + + .collators-box .card-title { + color: #333333; + } + + .collators-box h3{ + color: #333333; + font-weight: 500; } - .collators-box { - border: 1px solid #E5E5E5; + .collators-box h3.primary{ + color: #907EA0; + font-weight: 600; + } + + .collators-box p{ + color: #828282; + font-weight: 300; } .rewards-icon { @@ -49,9 +74,13 @@ .staked-icon { fill: #32253E; } - + .btn-unlock:disabled { background: #907EA0; color: #fff; } + + .claim-title { + color: #010E19; + } } From d9c9240a15eb787c9019d0d39f1b39b5f35e36b6 Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz <43585069+Sharqiewicz@users.noreply.github.com> Date: Thu, 15 Feb 2024 10:15:41 +0100 Subject: [PATCH 2/7] fix: Implement collapsible buttons for sidebar (#337) * use collapse for sidebar nav dropdown * fix displaying undefined class * show nav dropdown items in ul * remove sidebar item chevron --------- Co-authored-by: Kacper Szarkiewicz --- src/components/Layout/Nav.tsx | 22 ++++++++++++++-------- src/components/Layout/index.tsx | 4 ++-- src/components/Layout/links.tsx | 9 ++------- src/components/Layout/styles.sass | 1 + 4 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/components/Layout/Nav.tsx b/src/components/Layout/Nav.tsx index 41a3e8d3..8fe199c1 100644 --- a/src/components/Layout/Nav.tsx +++ b/src/components/Layout/Nav.tsx @@ -9,11 +9,13 @@ const CollapseMenu = ({ disabled, button, children, + ariaControls }: { link: string; disabled?: boolean; button: JSX.Element | null; children: JSX.Element | null; + ariaControls?: string; }) => { const { pathname } = useLocation(); const isActive = useMemo(() => { @@ -24,16 +26,19 @@ const CollapseMenu = ({ const [isOpen, { toggle }] = useBoolean(isActive); return ( -
+
-
{children}
-
+
{children}
+ ); }; @@ -48,7 +53,7 @@ const NavItem = ({ item, onClick }: { item: LinkItem; onClick?: () => void }) => {suffix} ); - const cls = `nav-item ${props?.className?.()}`; + const cls = `nav-item ${props?.className?.() || ''}`; return isExternal ? ( {linkUi} @@ -76,6 +81,7 @@ const Nav = memo(({ onClick }: NavProps) => { key={i} link={item.link} disabled={item.disabled} + ariaControls='submenu' button={ <> {item.prefix} @@ -84,11 +90,11 @@ const Nav = memo(({ onClick }: NavProps) => { } > -
+
+ ) : ( diff --git a/src/components/Layout/index.tsx b/src/components/Layout/index.tsx index 63db6435..3dd34825 100644 --- a/src/components/Layout/index.tsx +++ b/src/components/Layout/index.tsx @@ -34,7 +34,7 @@ export default function Layout(): JSX.Element | null { return (
+
diff --git a/src/components/Layout/links.tsx b/src/components/Layout/links.tsx index 05a4f974..8250164b 100644 --- a/src/components/Layout/links.tsx +++ b/src/components/Layout/links.tsx @@ -1,4 +1,3 @@ -import { ChevronRightIcon } from '@heroicons/react/20/solid'; import { ComponentChildren } from 'preact'; import { NavLinkProps } from 'react-router-dom'; import DashboardIcon from '../../assets/dashboard'; @@ -32,8 +31,6 @@ export type LinkItem = BaseLinkItem & { }; export type Links = (state: Partial) => LinkItem[]; -const arrow = ; - export const links: Links = ({ tenantName }) => [ { link: './dashboard', @@ -41,8 +38,7 @@ export const links: Links = ({ tenantName }) => [ props: { className: ({ isActive } = {}) => (isActive ? 'active' : ''), }, - prefix: , - suffix: arrow, + prefix: }, { link: 'https://app.zenlink.pro/', @@ -81,8 +77,7 @@ export const links: Links = ({ tenantName }) => [ props: { className: ({ isActive } = {}) => (isActive ? 'active' : ''), }, - prefix: , - suffix: arrow, + prefix: }, { link: `https://${tenantName}.polkassembly.io/`, diff --git a/src/components/Layout/styles.sass b/src/components/Layout/styles.sass index 6cabc74a..3e8100e6 100644 --- a/src/components/Layout/styles.sass +++ b/src/components/Layout/styles.sass @@ -119,6 +119,7 @@ nav opacity: 1 .collapse-btn + margin-top: 0 margin-bottom: 0 .disabled From 40bc2bb436da1ea0f827caf3d55e0d8107dcea53 Mon Sep 17 00:00:00 2001 From: Gonza Montiel Date: Fri, 16 Feb 2024 11:37:32 -0300 Subject: [PATCH 3/7] set max delegators for a candidate (#335) --- src/pages/collators/CollatorColumns.tsx | 31 ++++++++++++++++++++----- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/src/pages/collators/CollatorColumns.tsx b/src/pages/collators/CollatorColumns.tsx index ff6de7e9..9e7babcf 100644 --- a/src/pages/collators/CollatorColumns.tsx +++ b/src/pages/collators/CollatorColumns.tsx @@ -1,3 +1,4 @@ +import { InformationCircleIcon } from '@heroicons/react/20/solid'; import { WalletAccount } from '@talismn/connect-wallets'; import { ColumnDef } from '@tanstack/table-core'; import { StateUpdater } from 'preact/hooks'; @@ -8,6 +9,8 @@ import { ParachainStakingCandidate } from '../../hooks/staking/staking'; import { PalletIdentityInfo } from '../../hooks/useIdentityPallet'; import { nativeToFormat } from '../../shared/parseNumbers'; +const MAX_DELEGATORS_AMOUNT = 40; + export interface TCollator { candidate: ParachainStakingCandidate; collator: string; @@ -55,8 +58,23 @@ export const delegatorsColumn: ColumnDef = { header: 'Delegators', accessorKey: 'delegators', enableMultiSort: true, - accessorFn: ({ delegators }) => delegators?.toString(), + cell: ({ row }) => { + const maxDelegatorsReached = row.original.delegators >= MAX_DELEGATORS_AMOUNT; + return ( +
+
{row.original.delegators}
+ {maxDelegatorsReached && ( +
+ +
+ )} +
+ ); + }, }; export const apyColumn: ColumnDef = { @@ -98,8 +116,9 @@ export const actionsColumn = ({ enableSorting: false, accessorKey: 'actions', cell: ({ row }) => { - const showUnstake = Boolean(getAmountDelegated(row.original.candidate, userAccountAddress)); - const showStake = walletAccount && (!userStaking || showUnstake); + const maxDelegatorsReached = row.original.delegators >= MAX_DELEGATORS_AMOUNT; + const canUnstake = Boolean(getAmountDelegated(row.original.candidate, userAccountAddress)); + const canStake = walletAccount && !maxDelegatorsReached && (!userStaking || canUnstake); return (
@@ -123,7 +142,7 @@ export const actionsColumn = ({ onClick={() => { setSelectedCandidate(row.original.candidate); }} - disabled={!showStake} + disabled={!canStake} className="px-8 rounded-md" > Stake From 738d049b2f19c3bb129891503e0cf09dae8980c5 Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz <43585069+Sharqiewicz@users.noreply.github.com> Date: Mon, 19 Feb 2024 12:58:30 +0100 Subject: [PATCH 4/7] fix: handle walletconnect disconnect events (#338) * emit disconnect event to the wallet connected by WalletConnect * listen for disconnect event from wallet connected by WalletConnect * update walletconnect packages --------- Co-authored-by: Sharqiewicz --- package.json | 4 +- src/GlobalStateProvider.tsx | 23 ++++++- src/components/Wallet/WalletConnect/index.tsx | 62 ++++++++++++------ src/components/Wallet/index.tsx | 2 +- src/services/walletConnect/index.ts | 6 ++ yarn.lock | 64 +++++++++---------- 6 files changed, 105 insertions(+), 56 deletions(-) diff --git a/package.json b/package.json index 197271dd..6414208c 100644 --- a/package.json +++ b/package.json @@ -41,8 +41,8 @@ "@tanstack/react-query": "~4.32.6", "@tanstack/react-query-persist-client": "~4.32.6", "@tanstack/react-table": "^8.10.7", - "@walletconnect/modal": "^2.4.7", - "@walletconnect/universal-provider": "^2.8.1", + "@walletconnect/modal": "^2.6.2", + "@walletconnect/universal-provider": "^2.11.1", "big.js": "^6.2.1", "bn.js": "^5.2.1", "bs58": "^5.0.0", diff --git a/src/GlobalStateProvider.tsx b/src/GlobalStateProvider.tsx index aeda9771..2520688a 100644 --- a/src/GlobalStateProvider.tsx +++ b/src/GlobalStateProvider.tsx @@ -1,4 +1,5 @@ import { getWalletBySource, WalletAccount } from '@talismn/connect-wallets'; +import { getSdkError } from '@walletconnect/utils'; import { ComponentChildren, createContext } from 'preact'; import { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'preact/compat'; import { useLocation } from 'react-router-dom'; @@ -11,6 +12,9 @@ import { ThemeName } from './models/Theme'; import { storageService } from './services/storage/local'; import { walletConnectService } from './services/walletConnect'; +const SECONDS_IN_A_DAY = 86400; +const EXPIRATION_PERIOD = 2 * SECONDS_IN_A_DAY; // 2 days + export interface GlobalState { dAppName: string; tenantName: TenantName; @@ -64,15 +68,28 @@ const GlobalStateProvider = ({ children }: { children: ComponentChildren }) => { clear, } = useLocalStorage({ key: `${storageKeys.ACCOUNT}-${tenantName}`, - expire: 2 * 86400, // 2 days + expire: EXPIRATION_PERIOD, }); - const removeWalletAccount = useCallback(() => { + const handleWalletConnectDisconnect = useCallback(async () => { + if (walletAccount?.wallet?.extensionName === 'WalletConnect') { + const topic = walletConnectService.session?.topic; + if (topic) { + await walletConnectService.provider?.client.disconnect({ + topic, + reason: getSdkError('USER_DISCONNECTED'), + }); + } + } + }, [walletAccount]); + + const removeWalletAccount = useCallback(async () => { + await handleWalletConnectDisconnect(); clear(); // remove talisman storageService.remove('@talisman-connect/selected-wallet-name'); setWallet(undefined); - }, [clear]); + }, [clear, handleWalletConnectDisconnect]); const setWalletAccount = useCallback( (wallet: WalletAccount | undefined) => { diff --git a/src/components/Wallet/WalletConnect/index.tsx b/src/components/Wallet/WalletConnect/index.tsx index 3200e8b3..b66716a7 100644 --- a/src/components/Wallet/WalletConnect/index.tsx +++ b/src/components/Wallet/WalletConnect/index.tsx @@ -1,45 +1,71 @@ import { WalletConnectModal } from '@walletconnect/modal'; import UniversalProvider from '@walletconnect/universal-provider'; +import { SessionTypes } from '@walletconnect/types'; import { useCallback, useEffect, useState } from 'preact/compat'; import { toast } from 'react-toastify'; import logo from '../../../assets/wallet-connect.svg'; import { config } from '../../../config'; import { chainIds, walletConnectConfig } from '../../../config/walletConnect'; -import { GlobalState, useGlobalState } from '../../../GlobalStateProvider'; +import { useGlobalState } from '../../../GlobalStateProvider'; import { walletConnectService } from '../../../services/walletConnect'; -export type WalletConnectProps = { - setWalletAccount: GlobalState['setWalletAccount']; -}; - -const WalletConnect = ({ setWalletAccount }: WalletConnectProps) => { +const WalletConnect = () => { const [loading, setLoading] = useState(false); const [provider, setProvider] = useState | undefined>(); const [modal, setModal] = useState(); - const { tenantName } = useGlobalState(); + const { tenantName, setWalletAccount, removeWalletAccount } = useGlobalState(); - const walletConnectClick = useCallback(async () => { - setLoading(true); - try { - const chainId = chainIds[tenantName]; - if (!provider || !chainId) return; + const setupClientDisconnectListener = useCallback( + async (provider: Promise) => { + (await provider).client.on('session_delete', () => { + removeWalletAccount(); + }); + }, + [removeWalletAccount], + ); - const wcProvider = await provider; - const { uri, approval } = await wcProvider.client.connect(walletConnectConfig); - // if there is a URI from the client connect step open the modal + const handleModal = useCallback( + (uri?: string) => { if (uri) { modal?.openModal({ uri, onclose: () => setLoading(false) }); } + }, + [modal], + ); + + const handleSession = useCallback( + async (approval: () => Promise, chainId: string) => { const session = await approval(); setWalletAccount(await walletConnectService.init(session, chainId)); modal?.closeModal(); - setLoading(false); - // eslint-disable-next-line @typescript-eslint/no-explicit-any + }, + [setWalletAccount, modal], + ); + + const handleConnect = useCallback(async () => { + const chainId = chainIds[tenantName]; + if (!provider || !chainId) return; + + const wcProvider = await provider; + const { uri, approval } = await wcProvider.client.connect(walletConnectConfig); + + handleModal(uri); + handleSession(approval, chainId); + await setupClientDisconnectListener(provider); + }, [provider, tenantName, setupClientDisconnectListener, handleModal, handleSession]); + + const walletConnectClick = useCallback(async () => { + setLoading(true); + try { + await handleConnect(); + + //@eslint-disable-next-line no-explicit-any } catch (error: any) { toast(error, { type: 'error' }); + } finally { setLoading(false); } - }, [modal, provider, setWalletAccount, tenantName]); + }, [handleConnect]); useEffect(() => { if (provider) return; diff --git a/src/components/Wallet/index.tsx b/src/components/Wallet/index.tsx index e5b15ca0..4954b84c 100644 --- a/src/components/Wallet/index.tsx +++ b/src/components/Wallet/index.tsx @@ -76,7 +76,7 @@ const OpenWallet = ({ dAppName }: { dAppName: string }): JSX.Element => { )} - + } /> diff --git a/src/services/walletConnect/index.ts b/src/services/walletConnect/index.ts index f9c07338..52869091 100644 --- a/src/services/walletConnect/index.ts +++ b/src/services/walletConnect/index.ts @@ -7,6 +7,7 @@ import { config } from '../../config'; export const walletConnectService = { provider: undefined as UniversalProvider | undefined, + session: undefined as { topic: string } | undefined, getProvider: async function getProvider(): Promise { this.provider = this.provider || @@ -18,6 +19,11 @@ export const walletConnectService = { }, init: async function init(session: SessionTypes.Struct, chainId: string): Promise { const provider = await this.getProvider(); + + this.session = { + topic: session.topic, + }; + const wcAccounts = Object.values(session.namespaces) .map((namespace) => namespace.accounts) .flat(); diff --git a/yarn.lock b/yarn.lock index ef6bdf50..d2c9fa6e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5961,9 +5961,9 @@ __metadata: languageName: node linkType: hard -"@walletconnect/core@npm:2.11.0": - version: 2.11.0 - resolution: "@walletconnect/core@npm:2.11.0" +"@walletconnect/core@npm:2.11.1": + version: 2.11.1 + resolution: "@walletconnect/core@npm:2.11.1" dependencies: "@walletconnect/heartbeat": "npm:1.2.1" "@walletconnect/jsonrpc-provider": "npm:1.0.13" @@ -5976,13 +5976,13 @@ __metadata: "@walletconnect/relay-auth": "npm:^1.0.4" "@walletconnect/safe-json": "npm:^1.0.2" "@walletconnect/time": "npm:^1.0.2" - "@walletconnect/types": "npm:2.11.0" - "@walletconnect/utils": "npm:2.11.0" + "@walletconnect/types": "npm:2.11.1" + "@walletconnect/utils": "npm:2.11.1" events: "npm:^3.3.0" isomorphic-unfetch: "npm:3.1.0" lodash.isequal: "npm:4.5.0" uint8arrays: "npm:^3.1.0" - checksum: 673a9f3127a69a03de8de9626365b157ad6ce272b9ee04d3f67802685861f9f3ee748686662122d27212223f66054c49f0bf392f97e2fd18fab74789d0a87246 + checksum: 0f85627b6019143d1a5f96a3fefd613f387fa89609d37c66b185b67cfbc652abc28c61591b39ce673ab3fd51b331553f71216067d7ce1abfb5995f122ee0ec35 languageName: node linkType: hard @@ -6119,7 +6119,7 @@ __metadata: languageName: node linkType: hard -"@walletconnect/modal@npm:^2.4.7": +"@walletconnect/modal@npm:^2.6.2": version: 2.6.2 resolution: "@walletconnect/modal@npm:2.6.2" dependencies: @@ -6162,20 +6162,20 @@ __metadata: languageName: node linkType: hard -"@walletconnect/sign-client@npm:2.11.0": - version: 2.11.0 - resolution: "@walletconnect/sign-client@npm:2.11.0" +"@walletconnect/sign-client@npm:2.11.1": + version: 2.11.1 + resolution: "@walletconnect/sign-client@npm:2.11.1" dependencies: - "@walletconnect/core": "npm:2.11.0" + "@walletconnect/core": "npm:2.11.1" "@walletconnect/events": "npm:^1.0.1" "@walletconnect/heartbeat": "npm:1.2.1" "@walletconnect/jsonrpc-utils": "npm:1.0.8" "@walletconnect/logger": "npm:^2.0.1" "@walletconnect/time": "npm:^1.0.2" - "@walletconnect/types": "npm:2.11.0" - "@walletconnect/utils": "npm:2.11.0" + "@walletconnect/types": "npm:2.11.1" + "@walletconnect/utils": "npm:2.11.1" events: "npm:^3.3.0" - checksum: 92b8d66248b805849b70f35adc7f55bd7c9d6f35f5e980b1e90d71a86b008e43527b2dd8e47860d080cf296dcdf9ecfecb604b75ea0a1164c715dce4f66dadd0 + checksum: d4ff52265069c4b4adbd2d6ef349425ada21d5a38b3bc1078af33c01f333d93f83e874b06c90514b869b99feb207e5bf71634f6fc8dd1281eb7ec313e544853b languageName: node linkType: hard @@ -6188,9 +6188,9 @@ __metadata: languageName: node linkType: hard -"@walletconnect/types@npm:2.11.0": - version: 2.11.0 - resolution: "@walletconnect/types@npm:2.11.0" +"@walletconnect/types@npm:2.11.1": + version: 2.11.1 + resolution: "@walletconnect/types@npm:2.11.1" dependencies: "@walletconnect/events": "npm:^1.0.1" "@walletconnect/heartbeat": "npm:1.2.1" @@ -6198,30 +6198,30 @@ __metadata: "@walletconnect/keyvaluestorage": "npm:^1.1.1" "@walletconnect/logger": "npm:^2.0.1" events: "npm:^3.3.0" - checksum: 7fa2493d8a9c938821f5234b4d2a087f903359875925a7abea3a0640aa765886c01b4846bbe5e39923b48883f7fd92c3f4ff8e643c4c894c50e9f715b3a881d8 + checksum: 3fefd032886c90b4207474916d9d372ea4c0d8e33dd15547669b8f99dca6e95ac3a597442b0ee3c3af77f13e585d0cb54fdf5e382b08e0d3ed4a17d5b7db32f3 languageName: node linkType: hard -"@walletconnect/universal-provider@npm:^2.8.1": - version: 2.11.0 - resolution: "@walletconnect/universal-provider@npm:2.11.0" +"@walletconnect/universal-provider@npm:^2.11.1": + version: 2.11.1 + resolution: "@walletconnect/universal-provider@npm:2.11.1" dependencies: "@walletconnect/jsonrpc-http-connection": "npm:^1.0.7" "@walletconnect/jsonrpc-provider": "npm:1.0.13" "@walletconnect/jsonrpc-types": "npm:^1.0.2" "@walletconnect/jsonrpc-utils": "npm:^1.0.7" "@walletconnect/logger": "npm:^2.0.1" - "@walletconnect/sign-client": "npm:2.11.0" - "@walletconnect/types": "npm:2.11.0" - "@walletconnect/utils": "npm:2.11.0" + "@walletconnect/sign-client": "npm:2.11.1" + "@walletconnect/types": "npm:2.11.1" + "@walletconnect/utils": "npm:2.11.1" events: "npm:^3.3.0" - checksum: 78a3a16ef7a539caae0796745d80b211a918570bb2476ae064a56537e6aa1d038f53ed86588afd7f62cb833b2c690d9da3fee859a4a1926a79df79dd1f5176a9 + checksum: e11c3dc828de7d3b3094148e46ec2cac84f2b4f479e62caaa64e044f04553bef5d9948e42571d13b8a7f451a5942969a1268557cff27aa35d372447d7c588a16 languageName: node linkType: hard -"@walletconnect/utils@npm:2.11.0": - version: 2.11.0 - resolution: "@walletconnect/utils@npm:2.11.0" +"@walletconnect/utils@npm:2.11.1": + version: 2.11.1 + resolution: "@walletconnect/utils@npm:2.11.1" dependencies: "@stablelib/chacha20poly1305": "npm:1.0.1" "@stablelib/hkdf": "npm:1.0.1" @@ -6231,13 +6231,13 @@ __metadata: "@walletconnect/relay-api": "npm:^1.0.9" "@walletconnect/safe-json": "npm:^1.0.2" "@walletconnect/time": "npm:^1.0.2" - "@walletconnect/types": "npm:2.11.0" + "@walletconnect/types": "npm:2.11.1" "@walletconnect/window-getters": "npm:^1.0.1" "@walletconnect/window-metadata": "npm:^1.0.1" detect-browser: "npm:5.3.0" query-string: "npm:7.1.3" uint8arrays: "npm:^3.1.0" - checksum: 2219408f2a9bbca8d263a89dd54ae3e466f6d4b32b6b25f253d7f84f7e58c5836f4a08ab287c2d9ab5446c727624821597fa16d64d8c5ca748f8e1cba729a929 + checksum: b8e879c7a3e0f4fc498d1413d881092f6c0a13d16206a9a3cdbaa78334c5cb913a97f36b7f7f5c8aca3ac98162f5b8980445c0e8dca16d99dd9f4a66640b1fb3 languageName: node linkType: hard @@ -15236,8 +15236,8 @@ __metadata: "@types/testing-library__jest-dom": "npm:^5.14.5" "@typescript-eslint/eslint-plugin": "npm:^5.53.0" "@typescript-eslint/parser": "npm:^5.53.0" - "@walletconnect/modal": "npm:^2.4.7" - "@walletconnect/universal-provider": "npm:^2.8.1" + "@walletconnect/modal": "npm:^2.6.2" + "@walletconnect/universal-provider": "npm:^2.11.1" autoprefixer: "npm:^10.4.13" big.js: "npm:^6.2.1" bn.js: "npm:^5.2.1" From 692001ea6c8037d9c08a9b2bd5fc09265dcd2c3d Mon Sep 17 00:00:00 2001 From: Gonza Montiel Date: Mon, 19 Feb 2024 10:44:50 -0300 Subject: [PATCH 5/7] fix: Update disclaimer text for bridge --- src/pages/bridge/Issue/Disclaimer.tsx | 6 +++--- src/pages/bridge/Issue/index.tsx | 27 +++++++++++++++++++-------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/pages/bridge/Issue/Disclaimer.tsx b/src/pages/bridge/Issue/Disclaimer.tsx index 64aadecf..86fe6790 100644 --- a/src/pages/bridge/Issue/Disclaimer.tsx +++ b/src/pages/bridge/Issue/Disclaimer.tsx @@ -2,10 +2,10 @@ import { useCallback, useState } from 'preact/compat'; import BellIcon from '../../../assets/bell'; type Props = { - text: string; + content: JSX.Element; }; -export default function Disclaimer({ text }: Props) { +export default function Disclaimer({ content }: Props) { const [collapseVisibility, setCollapseVisibility] = useState(''); const toggle = useCallback(() => { @@ -30,7 +30,7 @@ export default function Disclaimer({ text }: Props) { Disclaimer
-

{text}

+

{content}

); } diff --git a/src/pages/bridge/Issue/index.tsx b/src/pages/bridge/Issue/index.tsx index 6ca07ed7..96799377 100644 --- a/src/pages/bridge/Issue/index.tsx +++ b/src/pages/bridge/Issue/index.tsx @@ -56,13 +56,24 @@ function Issue(props: IssueProps): JSX.Element { return amount ? decimalToStellarNative(amount) : Big(0); }, [amount]); - const disclaimerText = useMemo( - () => - `• Issue Fee: ${issueFee.mul(100)}% of the transaction amount. - • Redeem Fee: ${redeemFee.mul(100)}% of the transaction amount. - • Security deposit: ${issueGriefingCollateral.mul(100)}% of the transaction amount. - • Total issuable amount (in USD): 20,000 USD. - • Estimated time for issuing: 2 mins to 3 hrs (after submitting the Stellar payment to the vault).`, + const disclaimerContent = useMemo( + () => ( + + ), [issueFee, redeemFee, issueGriefingCollateral], ); @@ -175,7 +186,7 @@ function Issue(props: IssueProps): JSX.Element { ) : ( )} - +
From ce8418fa962825b9311a5b850630f9aed9c7f382 Mon Sep 17 00:00:00 2001 From: Kacper Szarkiewicz <43585069+Sharqiewicz@users.noreply.github.com> Date: Mon, 19 Feb 2024 15:06:22 +0100 Subject: [PATCH 6/7] Turn off autoComplete for Back to Stellar address input (#339) --- src/pages/bridge/Redeem/index.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/bridge/Redeem/index.tsx b/src/pages/bridge/Redeem/index.tsx index 1ad672d3..4a82dd66 100644 --- a/src/pages/bridge/Redeem/index.tsx +++ b/src/pages/bridge/Redeem/index.tsx @@ -152,7 +152,7 @@ function Redeem(props: RedeemProps): JSX.Element { Date: Tue, 20 Feb 2024 09:31:51 +0100 Subject: [PATCH 7/7] fix: add Buffer polyfill to window global object (#346) --- src/helpers/stellar.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/helpers/stellar.ts b/src/helpers/stellar.ts index cd079997..cf70686d 100644 --- a/src/helpers/stellar.ts +++ b/src/helpers/stellar.ts @@ -1,6 +1,10 @@ import { Asset, Keypair, StrKey } from 'stellar-sdk'; import { Buffer } from 'buffer'; +// Applying this assignment to the global object here - because Buffer is used only in this file. If Buffer is to be used in several places - it should be added to the global object in the entry file of the application. +// Ref: https://github.com/pendulum-chain/portal/issues/344 +window.Buffer = Buffer; + export const StellarPublicKeyPattern = /^G[A-Z0-9]{55}$/; export const isPublicKey = (str: string) => Boolean(str.match(StellarPublicKeyPattern));