Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/staging' into 257-metamask-snap-…
Browse files Browse the repository at this point in the history
…integration
  • Loading branch information
gonzamontiel committed Feb 20, 2024
2 parents b272390 + 98e6594 commit f9624be
Show file tree
Hide file tree
Showing 20 changed files with 344 additions and 183 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,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",
Expand Down
23 changes: 20 additions & 3 deletions src/GlobalStateProvider.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -12,6 +13,9 @@ import { initiateMetamaskInjectedAccount, WALLET_SOURCE_METAMASK } from './servi
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;
Expand Down Expand Up @@ -73,16 +77,29 @@ const GlobalStateProvider = ({ children }: { children: ComponentChildren }) => {
clear,
} = useLocalStorage<string | undefined>({
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');
storageService.remove(`${tenantName}-metamask-wallet`);
setWallet(undefined);
}, [clear]);
}, [clear, handleWalletConnectDisconnect]);

const setWalletAccount = useCallback(
(wallet: WalletAccount | undefined) => {
Expand Down
22 changes: 14 additions & 8 deletions src/components/Layout/Nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(() => {
Expand All @@ -24,16 +26,19 @@ const CollapseMenu = ({
const [isOpen, { toggle }] = useBoolean(isActive);

return (
<div className={disabled ? 'disabled' : ''}>
<section className={`collapse ${disabled ? 'disabled' : 'collapse-arrow'} ${isOpen ? 'collapse-open' : ''}`}>
<button
type="button"
className={`nav-item collapse-btn mb-0 ${isActive ? 'active' : ''}`}
className={`nav-item collapse-btn collapse-title ${isActive ? 'active' : ''}`}
onClick={() => toggle()}
aria-controls={ariaControls}
aria-expanded={isOpen}
aria-disabled={disabled}
>
{button}
</button>
<div className={`${isOpen ? '' : 'hidden'}`}>{children}</div>
</div>
<div className='collapse-content p-0'>{children}</div>
</section>
);
};

Expand All @@ -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 ? (
<a href={link} {...props} className={cls} onClick={onClick}>
{linkUi}
Expand Down Expand Up @@ -76,6 +81,7 @@ const Nav = memo(({ onClick }: NavProps) => {
key={i}
link={item.link}
disabled={item.disabled}
ariaControls='submenu'
button={
<>
{item.prefix}
Expand All @@ -84,11 +90,11 @@ const Nav = memo(({ onClick }: NavProps) => {
</>
}
>
<div className="submenu">
<ul className="submenu" id='submenu'>
{item.submenu.map((subItem, j) => (
<NavItem key={`${i}-${j}`} item={subItem} onClick={onClick} />
<li key={`${i}-${j}`}><NavItem item={subItem} onClick={onClick} /></li>
))}
</div>
</ul>
</CollapseMenu>
) : (
<NavItem key={i} item={item} onClick={onClick} />
Expand Down
4 changes: 2 additions & 2 deletions src/components/Layout/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export default function Layout(): JSX.Element | null {
return (
<div id="main-wrapper" className="flex">
<div id="sidebar-wrapper" className="flex flex-wrap z-50">
<div
<aside
style={{
...(isPendulum ? null : { backgroundColor: '#1c1c1c' }),
}}
Expand Down Expand Up @@ -62,7 +62,7 @@ export default function Layout(): JSX.Element | null {
<NetworkId />
<SocialAndTermLinks />
</div>
</div>
</aside>
</div>
<section>
<header>
Expand Down
9 changes: 2 additions & 7 deletions src/components/Layout/links.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -32,17 +31,14 @@ export type LinkItem = BaseLinkItem & {
};
export type Links = (state: Partial<GlobalState>) => LinkItem[];

const arrow = <ChevronRightIcon className="nav-arrow w-5 h-5" />;

export const links: Links = ({ tenantName }) => [
{
link: './dashboard',
title: 'Dashboard',
props: {
className: ({ isActive } = {}) => (isActive ? 'active' : ''),
},
prefix: <DashboardIcon />,
suffix: arrow,
prefix: <DashboardIcon />
},
{
link: 'https://app.zenlink.pro/',
Expand Down Expand Up @@ -81,8 +77,7 @@ export const links: Links = ({ tenantName }) => [
props: {
className: ({ isActive } = {}) => (isActive ? 'active' : ''),
},
prefix: <StakingIcon />,
suffix: arrow,
prefix: <StakingIcon />
},
{
link: `https://${tenantName}.polkassembly.io/`,
Expand Down
1 change: 1 addition & 0 deletions src/components/Layout/styles.sass
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ nav
opacity: 1

.collapse-btn
margin-top: 0
margin-bottom: 0

.disabled
Expand Down
62 changes: 44 additions & 18 deletions src/components/Wallet/WalletConnect/index.tsx
Original file line number Diff line number Diff line change
@@ -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<Promise<UniversalProvider> | undefined>();
const [modal, setModal] = useState<WalletConnectModal | undefined>();
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<UniversalProvider>) => {
(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<SessionTypes.Struct>, 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;
Expand Down
4 changes: 2 additions & 2 deletions src/components/Wallet/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { WalletSelect } from '@talismn/connect-components';
import { Button, Divider, Dropdown } from 'react-daisyui';
import { isMobile } from 'react-device-detect';
import { useGlobalState } from '../../GlobalStateProvider';
import { getAddressForFormat } from '../../helpers/addressFormatter';
import { useNodeInfoState } from '../../NodeInfoProvider';
import { getAddressForFormat } from '../../helpers/addressFormatter';
import { useAccountBalance } from '../../shared/useAccountBalance';
import { CopyableAddress } from '../PublicKey';
import { Skeleton } from '../Skeleton';
Expand Down Expand Up @@ -79,7 +79,7 @@ const OpenWallet = ({ dAppName }: { dAppName: string }): JSX.Element => {
)}
<MetamaskWallet setWalletAccount={setWalletAccount} />
<Divider className="before:bg-transparent after:bg-transparent h-2" />
<WalletConnect setWalletAccount={setWalletAccount} />
<WalletConnect />
</>
}
/>
Expand Down
4 changes: 4 additions & 0 deletions src/helpers/stellar.ts
Original file line number Diff line number Diff line change
@@ -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));
Expand Down
6 changes: 3 additions & 3 deletions src/pages/bridge/Issue/Disclaimer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(() => {
Expand All @@ -30,7 +30,7 @@ export default function Disclaimer({ text }: Props) {
<BellIcon />
<strong className="ml-2">Disclaimer</strong>
</div>
<p className="text-sm collapse-content whitespace-pre-line">{text}</p>
<p className="text-sm collapse-content whitespace-pre-line">{content}</p>
</div>
);
}
27 changes: 19 additions & 8 deletions src/pages/bridge/Issue/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(
() => (
<ul>
<li>• Bridge Fee: Currently free, transitioning to 0.1% per transaction soon.</li>
<li>• Security deposit: 0.5% of the transaction amount locked, returned after successful issue/redeem </li>
<li>
• Total issuable amount (in USD): 20000 USD. Join our vault operator program, more
<a
target="_blank"
className="text-accent ml-1"
href="https://pendulum.gitbook.io/pendulum-docs/build/spacewalk-stellar-bridge/operating-a-vault"
>
here
</a>
</li>
<li>• Estimated time for issuing: 2 mins to 3 hrs (after submitting the Stellar payment to the vault).`</li>
</ul>
),
[issueFee, redeemFee, issueGriefingCollateral],
);

Expand Down Expand Up @@ -175,7 +186,7 @@ function Issue(props: IssueProps): JSX.Element {
) : (
<OpenWallet dAppName={dAppName} />
)}
<Disclaimer text={disclaimerText} />
<Disclaimer content={disclaimerContent} />
</form>
</div>
</div>
Expand Down
3 changes: 2 additions & 1 deletion src/pages/bridge/Redeem/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ function Redeem(props: RedeemProps): JSX.Element {
<label className="label flex align-center">
<span className="text-sm">{`Max redeemable: ${nativeToDecimal(
selectedVault?.redeemableTokens?.toString() || 0,
).toFixed(2)}
).toFixed(2)}
${selectedAsset?.code}`}</span>
</label>
<LabelledInputField
Expand All @@ -163,6 +163,7 @@ function Redeem(props: RedeemProps): JSX.Element {
type="text"
style={{ marginTop: 8 }}
className="border-base-400 bg-base-200"
autoComplete="off"
/>
<FeeBox
amountNative={amountNative}
Expand Down
Loading

0 comments on commit f9624be

Please sign in to comment.