Skip to content

Commit 43a608e

Browse files
authored
Feat/demo siwe (#136)
* feat: add ens support * fix: throw queryparam errors in getAccess * fix: improve error on connect * feat: add siwe to demo * fix: missing type
1 parent a83d2e5 commit 43a608e

8 files changed

Lines changed: 203 additions & 46 deletions

File tree

apps/customer/app/(authorized)/dashboard/page.tsx

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22

33
import { useState } from 'react';
44
import Box from '@mui/material/Box';
5+
import Button from '@mui/material/Button';
56
import Stack from '@mui/material/Stack';
67

78
import { Currency } from '@monerium/sdk';
9+
import { useAuth } from '@monerium/sdk-react-provider';
810

911
import ChainFilter from 'components/Dashboard/Filters/ChainFilter';
1012
import TokenFilter from 'components/Dashboard/Filters/TokenFilter';
@@ -24,6 +26,8 @@ function Dashboard() {
2426
selectedCurrency
2527
);
2628

29+
const { revokeAccess } = useAuth();
30+
2731
return (
2832
<Box sx={{ pt: 7 }}>
2933
<Stack direction="row" sx={{ p: 3 }}>
@@ -42,6 +46,27 @@ function Dashboard() {
4246
selectedChain={selectedChain}
4347
selectedCurrency={selectedCurrency}
4448
/>
49+
<Box
50+
sx={{
51+
display: 'flex',
52+
justifyContent: 'center',
53+
mt: 2, // adds some margin top, adjust as needed
54+
}}
55+
>
56+
<Button
57+
color="error"
58+
size="large"
59+
variant="outlined"
60+
onClick={() => {
61+
window.localStorage.removeItem(
62+
'monerium.insecurely_store_refresh_token'
63+
);
64+
revokeAccess();
65+
}}
66+
>
67+
Logout
68+
</Button>
69+
</Box>
4570
</Box>
4671
);
4772
}
Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
'use client';
22

3+
import { useEffect } from 'react';
34
import { useRouter } from 'next/navigation';
45
import Box from '@mui/material/Box';
56
import Paper from '@mui/material/Paper';
@@ -14,28 +15,31 @@ export default function AuthorizedLayout({
1415
}: Readonly<{
1516
children: React.ReactNode;
1617
}>) {
18+
const router = useRouter();
1719
const { isAuthorized, isLoading } = useAuth();
1820

19-
const router = useRouter();
21+
useEffect(() => {
22+
if (!isAuthorized && !isLoading) {
23+
router.push('/');
24+
}
25+
}, [isAuthorized, isLoading, router]);
2026

27+
// Render loading screen if still loading
2128
if (isLoading) {
2229
return <LoadingScreen />;
2330
}
2431

25-
if (!isAuthorized && !isLoading) {
26-
router?.push('/');
32+
// Return null if not authorized
33+
if (!isAuthorized) {
34+
return null;
2735
}
2836

29-
if (isAuthorized) {
30-
return (
31-
<>
32-
<Box sx={{ pb: 7 }}>
33-
{children}
34-
<Paper></Paper>
35-
<BottomNavigation />
36-
</Box>
37-
</>
38-
);
39-
}
40-
return null;
37+
// Render main content if authorized
38+
return (
39+
<Box sx={{ pb: 7 }}>
40+
{children}
41+
<Paper></Paper>
42+
<BottomNavigation />
43+
</Box>
44+
);
4145
}

apps/customer/components/Dashboard/WalletList/WalletItem.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
'use client';
22

3-
import { ListItemText } from '@mui/material';
43
import Avatar from '@mui/material/Avatar';
54
import ListItemAvatar from '@mui/material/ListItemAvatar';
65
import ListItemButton from '@mui/material/ListItemButton';
6+
import ListItemText from '@mui/material/ListItemText';
77

88
import { Currency, shortenAddress } from '@monerium/sdk';
99
import { useBalances } from '@monerium/sdk-react-provider';
1010

11+
import Punk from 'components/Punk';
1112
import { useEns } from 'hooks/useEns';
1213

1314
const WalletItem = ({
@@ -31,6 +32,7 @@ const WalletItem = ({
3132
<ListItemButton key={address}>
3233
<ListItemAvatar>
3334
<Avatar alt="Currency" src={`/tokens/${currency}.png`} />
35+
{/* <Punk address={address} size={32} /> */}
3436
</ListItemAvatar>
3537
<ListItemText
3638
primary={ensName || shortenAddress(address)}
Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,23 @@
11
.wrapper {
22
display: flex;
3-
justify-content: center;
3+
flex-direction: column;
44
align-items: center;
5-
flex-grow: 1;
5+
justify-content: center;
6+
gap: 1rem;
7+
padding: 4rem;
68

79
:global(.MuiButton-root) {
810
margin: 16px;
911
border-radius: 50px;
1012
text-wrap: nowrap;
1113
}
14+
}
15+
16+
.topwrapper {
17+
display: flex;
18+
justify-content: center;
19+
align-items: center;
20+
flex-grow: 1;
21+
22+
1223
}
Lines changed: 93 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,107 @@
11
'use client';
22
import React from 'react';
33
import Image from 'next/image';
4+
import { useSignMessage } from 'wagmi';
5+
import { useAccount } from 'wagmi';
6+
import Alert from '@mui/material/Alert';
7+
import Box from '@mui/material/Box';
48
import Button from '@mui/material/Button';
9+
import Snackbar, { SnackbarCloseReason } from '@mui/material/Snackbar';
510

11+
import { siweMessage } from '@monerium/sdk';
612
import { useAuth } from '@monerium/sdk-react-provider';
713

814
import s from './MoneriumConnect.module.scss';
915
export const MoneriumConnect = () => {
10-
const { authorize } = useAuth();
16+
const { address } = useAccount();
17+
const { authorize, siwe, error } = useAuth();
18+
const { signMessageAsync } = useSignMessage();
19+
const [open, setOpen] = React.useState(!!(error as Error)?.message);
20+
21+
const handleClose = (
22+
event?: React.SyntheticEvent | Event,
23+
reason?: SnackbarCloseReason
24+
) => {
25+
if (reason === 'clickaway') {
26+
return;
27+
}
28+
29+
setOpen(false);
30+
};
31+
32+
const message = siweMessage({
33+
domain: 'localhost:3000',
34+
address: `${address}`,
35+
appName: 'SDK TEST APP',
36+
redirectUri: 'http://localhost:3000/dashboard',
37+
chainId: 1,
38+
privacyPolicyUrl: 'https://example.com/privacy-policy',
39+
termsOfServiceUrl: 'https://example.com/terms-of-service',
40+
});
41+
42+
const signInWithEthereum = () => {
43+
signMessageAsync({
44+
message: message,
45+
}).then((signature) => {
46+
siwe({
47+
message: message,
48+
signature: signature,
49+
});
50+
});
51+
};
1152

1253
return (
13-
<div className={s.wrapper}>
14-
<Button
15-
href="https://monerium.com"
16-
size="large"
17-
variant="contained"
18-
onClick={() => {}}
19-
>
20-
Read more
21-
</Button>
22-
<Button
23-
size="large"
24-
variant="outlined"
25-
onClick={() => authorize()}
26-
startIcon={
27-
<Image
28-
src="/monerium-icon.png"
29-
alt="Monerium icon"
30-
width={16}
31-
height={20}
32-
priority
33-
/>
34-
}
54+
<>
55+
<Snackbar
56+
color="error"
57+
open={open}
58+
autoHideDuration={6000}
59+
onClose={handleClose}
3560
>
36-
Connect
37-
</Button>
38-
</div>
61+
<Alert
62+
onClose={handleClose}
63+
severity="error"
64+
variant="filled"
65+
sx={{ width: '100%' }}
66+
>
67+
{(error as Error)?.message}
68+
</Alert>
69+
</Snackbar>
70+
<Box className={s.wrapper}>
71+
<Box className={s.topwrapper}>
72+
<Button
73+
href="https://monerium.com"
74+
size="large"
75+
variant="contained"
76+
onClick={() => {}}
77+
>
78+
Read more
79+
</Button>
80+
<Button
81+
size="large"
82+
variant="outlined"
83+
onClick={() => authorize()}
84+
startIcon={
85+
<Image
86+
src="/monerium-icon.png"
87+
alt="Monerium icon"
88+
width={16}
89+
height={20}
90+
priority
91+
/>
92+
}
93+
>
94+
Connect
95+
</Button>
96+
</Box>
97+
<Button
98+
size="large"
99+
variant="outlined"
100+
onClick={() => signInWithEthereum()}
101+
>
102+
Sign In with Ethereum
103+
</Button>
104+
</Box>
105+
</>
39106
);
40107
};
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/* eslint-disable @next/next/no-img-element */
2+
import React from 'react';
3+
4+
export default function Punk({
5+
address,
6+
size,
7+
}: {
8+
address: string;
9+
size: number;
10+
}) {
11+
const part1 = address.substr(2, 20);
12+
const part2 = address.substr(22);
13+
14+
const x = parseInt(part1, 16) % 100;
15+
const y = parseInt(part2, 16) % 100;
16+
17+
return (
18+
<div
19+
style={{
20+
position: 'relative',
21+
width: size,
22+
height: size,
23+
overflow: 'hidden',
24+
}}
25+
>
26+
<img
27+
src="https://punkwallet.io/punks.png"
28+
style={{
29+
position: 'absolute',
30+
left: -size * x,
31+
top: -size * y,
32+
width: size * 100,
33+
height: size * 100,
34+
imageRendering: 'pixelated',
35+
}}
36+
/>
37+
</div>
38+
);
39+
}

packages/sdk-react-provider/src/lib/provider.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ export const MoneriumProvider = ({
6363
try {
6464
setIsAuthorized(await sdk.getAccess());
6565
} catch (error) {
66-
console.error('Failed to get access:', error);
66+
setError(error);
6767
} finally {
6868
setLoadingAuth(false);
6969
if (sdk?.bearerProfile) {
@@ -88,7 +88,7 @@ export const MoneriumProvider = ({
8888
try {
8989
setIsAuthorized(await sdk.getAccess(refreshToken));
9090
} catch (error) {
91-
console.error('Failed to get access:', error);
91+
setError(error);
9292
} finally {
9393
if (sdk?.bearerProfile) {
9494
onRefreshTokenUpdate?.(sdk.bearerProfile.refresh_token);

packages/sdk/src/client.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,15 @@ export class MoneriumClient {
296296
throw new Error('This only works client side');
297297
}
298298

299+
const error =
300+
new URLSearchParams(window.location.search).get('error') || undefined;
301+
const errorDescription =
302+
new URLSearchParams(window.location.search).get('error_description') ||
303+
undefined;
304+
if (error || errorDescription) {
305+
throw new Error(errorDescription);
306+
}
307+
299308
const authCode =
300309
new URLSearchParams(window.location.search).get('code') || undefined;
301310

0 commit comments

Comments
 (0)