Skip to content

Commit

Permalink
wallet connect 2 support
Browse files Browse the repository at this point in the history
  • Loading branch information
juliancwirko committed Mar 5, 2023
1 parent a3bfeb1 commit 2506bc4
Show file tree
Hide file tree
Showing 11 changed files with 2,612 additions and 270 deletions.
4 changes: 4 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
# MultiversX chain (can be devnet, testnet, mainnet)
NEXT_PUBLIC_MULTIVERSX_CHAIN = devnet

# Wallet Connect 2 Project Id. This one will work only with this project
# Get yours at: https://cloud.walletconnect.com/sign-in
NEXT_PUBLIC_WC_PROJECT_ID = 559caed757a8bc36522ec786b2af1b9b

#
# Either the public API endpoint of your MultiversX api
# or the masked proxy that will be used instead.
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
### [6.0.0](https://github.com/ElvenTools/elven-tools-dapp/releases/tag/v6.0.0) (2023-03-05)
- switch to v0.1.0 of [useElven](https://www.useelven.com/) with support for xPortal when signing
- changes for Wallet Connect pairings list
- other minor improvements

### [5.1.0](https://github.com/ElvenTools/elven-tools-dapp/releases/tag/v5.1.0) (2023-03-04)
- fix passing custom configuration, one should use .env variables for that, `useNetworkSync` will read from them

Expand Down
73 changes: 6 additions & 67 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,77 +21,15 @@ The Dapp is built using Nextjs and a couple of helpful tools. It allows you to i
- Preconfigured UI based on Chakra UI
- The template with sections mainly used on minter dapps (it will be developed further)

### Examples
### @useelven/hooks

Login with one of four methods.
The template is based on `@useelven/core` npm library.

```jsx
const { login, isLoggedIn, error, walletConnectUri, getHWAccounts } = useLogin();
- [@useelven/hooks docs](https://www.useElven.com) - React hooks for MultiversX blockchain

(...)
Besides that, there are custom React components and hooks that will help you with development.

login(LoginMethodsEnum.ledger)
```

Custom mint transactions for the Elven Tools Smart Contract. There is also a more generic `useScTransaction` hook.

```jsx
const { mint, pending, transaction, error } = useMintTransaction();

(...)

mint(amount)
```

Query the Elven Tools Smart Contract. There is also a more generic `useScQuery` hook.

```jsx
const {
data,
fetch,
isLoading,
} = useElvenScQuery<boolean>({
funcName: 'isAllowlistEnabled',
type: SCQueryType.BOOLEAN,
autoInit: false,
});

(...)

fetch()
```

You can also query more complex data types. Then you will need to provide the ABI JSON file.

```jsx
import { TypedOutcomeBundle } from '@multiversx/sdk-core';
import abiJSON from '../config/abi.json';

const { data } = useScQuery<TypedOutcomeBundle>({
type: SCQueryType.COMPLEX,
payload: {
scAddress: 'erd1qqq...',
funcName: 'yourScFunction',
args: [], // args in hex format, use erdjs for conversion, see above
},
autoInit: true,
abiJSON,
});
```

The `data` here will be a `TypedOutcomeBundle`. Which is:

```typescript
interface TypedOutcomeBundle {
returnCode: ReturnCode;
returnMessage: string;
values: TypedValue[];
firstValue?: TypedValue;
secondValue?: TypedValue;
thirdValue?: TypedValue;
lastValue?: TypedValue;
}
```
### Elven Tools Dapp docs

For more docs on how to use it check the link above, and for more examples see: [elven.tools/docs/dapp-react-hooks-and-components.html](https://elven.tools/docs/dapp-react-hooks-and-components.html)

Expand Down Expand Up @@ -128,6 +66,7 @@ More docs on it: [Minter Dapp introduction](https://www.elven.tools/docs/minter-

### Other tools

- [useElven](https://www.useElven.com) - React core hooks for MultiversX blockchain
- [elven.js](https://www.elvenjs.com) - standalone lite SDK for browsers without build steps
- [Buildo Begins](https://github.com/xdevguild/buildo-begins) - CLI helper tools - interaction with APIs, smart contracts and protocol
- [Nextjs Dapp Template](https://github.com/xdevguild/nextjs-dapp-template) - Open source Dapp template for the MultiversX blockchain (more general one).
Expand Down
49 changes: 41 additions & 8 deletions components/core/LoginComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,26 @@
import { useCallback, memo, useState } from 'react';
import { Box, Stack } from '@chakra-ui/react';
import { useLogin, LoginMethodsEnum } from '@useelven/core';
import { MobileLoginQR } from './MobileLoginQR';
import { WalletConnectQRCode } from './WalletConnectQRCode';
import { WalletConnectPairings } from './WalletConnectPairings';
import { ActionButton } from '../ActionButton';
import { LedgerAccountsList } from './LedgerAccountsList';

export const LoginComponent = memo(() => {
// If you need the auth signature and token you can pass it here
// example: const { ... } = useLogin({ token: "some_hash_here" })
// all auth providers will return the signature, it will be saved in localstorage and global state
const { login, isLoggedIn, error, walletConnectUri, getHWAccounts } =
useLogin();
// If you need the auth signature and token pas your unique token in useLogin
// For the demo purposes here is a dummy token
const {
login,
isLoggedIn,
error,
walletConnectUri,
getHWAccounts,
setLoggingInState,
walletConnectPairings,
walletConnectPairingLogin,
walletConnectRemovePairing,
} = useLogin({ token: 'token_just_for_testing_purposes' });

const [loginMethod, setLoginMethod] = useState<LoginMethodsEnum>();

const handleLogin = useCallback(
Expand All @@ -31,7 +41,19 @@ export const LoginComponent = memo(() => {
setLoginMethod(undefined);
}, []);

if (error) return <Box textAlign="center">{error}</Box>;
const backToOptions = useCallback(() => {
setLoggingInState('error', '');
}, [setLoggingInState]);

if (error)
return (
<Stack>
<Box textAlign="center">{error}</Box>
<ActionButton isFullWidth onClick={backToOptions}>
Back
</ActionButton>
</Stack>
);

return (
<>
Expand Down Expand Up @@ -64,9 +86,20 @@ export const LoginComponent = memo(() => {
</Stack>
{loginMethod === LoginMethodsEnum.walletconnect && walletConnectUri && (
<Box mt={5}>
<MobileLoginQR walletConnectUri={walletConnectUri} />
<WalletConnectQRCode uri={walletConnectUri} />
</Box>
)}

{loginMethod === LoginMethodsEnum.walletconnect &&
walletConnectPairings &&
walletConnectPairings.length > 0 && (
<WalletConnectPairings
pairings={walletConnectPairings}
login={walletConnectPairingLogin}
remove={walletConnectRemovePairing}
/>
)}

{loginMethod === LoginMethodsEnum.ledger && (
<>
<LedgerAccountsList
Expand Down
35 changes: 27 additions & 8 deletions components/core/LoginModalButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ import {
Spinner,
Flex,
ModalHeader,
Stack,
} from '@chakra-ui/react';
import { FC } from 'react';
import { ActionButton } from '../ActionButton';
import { LoginComponent } from './LoginComponent';
import { useEffectOnlyOnUpdate } from '../../hooks/useEffectOnlyOnUpdate';
import { useLogin, useLogout } from '@useelven/core';
import { getSigningDeviceName } from '../../utils/getSigningDeviceName';
import { useLogin, useLogout, useLoginInfo } from '@useelven/core';

interface LoginModalButtonProps {
onClose?: () => void;
Expand All @@ -29,7 +31,8 @@ export const LoginModalButton: FC<LoginModalButtonProps> = ({
onClose,
onOpen,
}) => {
const { isLoggedIn, isLoggingIn } = useLogin();
const { isLoggedIn, isLoggingIn, setLoggingInState } = useLogin();
const { loginMethod } = useLoginInfo();
const { logout } = useLogout();
const {
isOpen: opened,
Expand All @@ -43,6 +46,12 @@ export const LoginModalButton: FC<LoginModalButtonProps> = ({
}
}, [isLoggedIn]);

const onCloseComplete = () => {
setLoggingInState('error', '');
};

const ledgerOrPortalName = getSigningDeviceName(loginMethod);

return (
<>
{isLoggedIn ? (
Expand All @@ -56,6 +65,7 @@ export const LoginModalButton: FC<LoginModalButtonProps> = ({
onClose={close}
isCentered
scrollBehavior="inside"
onCloseComplete={onCloseComplete}
>
<CustomModalOverlay />
<ModalContent
Expand All @@ -80,13 +90,22 @@ export const LoginModalButton: FC<LoginModalButtonProps> = ({
justifyContent="center"
position="absolute"
inset={0}
zIndex="overlay"
>
<Spinner
thickness="3px"
speed="0.4s"
color="elvenTools.color2.base"
size="xl"
/>
<Stack alignItems="center">
{ledgerOrPortalName ? (
<>
<Text fontSize="lg">Confirmation required</Text>
<Text fontSize="sm">Approve on {ledgerOrPortalName}</Text>
</>
) : null}
<Spinner
thickness="3px"
speed="0.4s"
color="elvenTools.color2.base"
size="xl"
/>
</Stack>
</Flex>
)}
<LoginComponent />
Expand Down
71 changes: 71 additions & 0 deletions components/core/WalletConnectPairings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { FC, MouseEventHandler } from 'react';
import { PairingTypes } from '@useelven/core';
import { Stack, Box, Text, Heading, IconButton } from '@chakra-ui/react';
import { CloseIcon } from '@chakra-ui/icons';

interface WalletConnectPairingsProps {
pairings: PairingTypes.Struct[];
login: (topic: string) => Promise<void>;
remove: (topic: string) => Promise<void>;
}

export const WalletConnectPairings: FC<WalletConnectPairingsProps> = ({
pairings,
login,
remove,
}) => {
const handleLogin = (topic: string) => () => {
login(topic);
};

const handleRemove =
(topic: string): MouseEventHandler<HTMLButtonElement> | undefined =>
(e) => {
e.stopPropagation();
remove(topic);
};

return (
<Stack>
{pairings?.length > 0 && (
<Heading size="md" mt={4}>
Existing pairings:
</Heading>
)}
{pairings.map((pairing) => (
<Box
bgColor="elvenTools.white"
py={2}
px={4}
pr={8}
borderRadius="md"
key={pairing.topic}
cursor="pointer"
onClick={handleLogin(pairing.topic)}
userSelect="none"
position="relative"
>
<IconButton
position="absolute"
top={2}
right={2}
aria-label="remove-pairing"
color="elvenTools.dark.base"
h={6}
minW={6}
icon={<CloseIcon boxSize={2} />}
onClick={handleRemove(pairing.topic)}
/>
<Text fontSize="lg" color="elvenTools.dark.base">
{pairing.peerMetadata?.name}
</Text>
{pairing.peerMetadata?.url ? (
<Text fontSize="xs" color="elvenTools.dark.base">
({pairing.peerMetadata.url})
</Text>
) : null}
</Box>
))}
</Stack>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,30 @@ import { useConfig } from '@useelven/core';
import { isMobile } from '../../utils/isMobile';
import QRCode from 'qrcode';

interface MobileLoginQRProps {
walletConnectUri: string;
interface WalletConnectQRCodeProps {
uri: string;
}

export const MobileLoginQR: FunctionComponent<MobileLoginQRProps> = ({
walletConnectUri,
}) => {
export const WalletConnectQRCode: FunctionComponent<
WalletConnectQRCodeProps
> = ({ uri }) => {
const [qrCodeSvg, setQrCodeSvg] = useState('');
const { walletConnectDeepLink } = useConfig();

useEffect(() => {
const generateQRCode = async () => {
if (!walletConnectUri) {
if (!uri) {
return;
}

const svg = await QRCode.toString(walletConnectUri, {
const svg = await QRCode.toString(uri, {
type: 'svg',
});

setQrCodeSvg(svg);
};
generateQRCode();
}, [walletConnectUri]);
}, [uri]);

const mobile = isMobile();

Expand Down Expand Up @@ -60,7 +60,7 @@ export const MobileLoginQR: FunctionComponent<MobileLoginQRProps> = ({
transition="background-color .3s"
as="a"
href={`${walletConnectDeepLink}?wallet-connect=${encodeURIComponent(
walletConnectUri
uri
)}`}
rel="noopener noreferrer nofollow"
target="_blank"
Expand Down
Loading

0 comments on commit 2506bc4

Please sign in to comment.