Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: WalletConnect integration, part 1, session proposal #1959

Merged
merged 2 commits into from
Dec 2, 2024

Conversation

dianasavvatina
Copy link
Contributor

@dianasavvatina dianasavvatina commented Sep 27, 2024

This is the first part of the WalletConnect integration. It includes the following components:

  • initiating WalletKit by WalletConnect
  • subscribing to basic events
  • handling session proposal on a basic level

Limitations:

  • all requests are rejected
  • no pairing list
  • no way to disconnect
  • no verification of dapp, no check for scam

Proposed changes

Task link

Types of changes

  • Bugfix
  • New feature
  • Refactor
  • Breaking change
  • UI fix

Steps to reproduce

  1. Rejecting
    actions: on dApp - connect, copy link. on Wallet: Connect, reject
    result: on Wallet - Approve button is inactive. On reject - modal is closed immediately. on dApp: modal is closed

  2. Approving
    actions: on dApp - connect, copy link. on Wallet: on Wallet: Connect, select Account, Approve
    result: on dApp - connected, the list of actions is shown. on Wallet: the modal is closed
    actions: on dApp - request transaction
    result: on dApp - transaction is immediately rejected with USER rejected error

To be covered in the next PR, #1985:

  • connected dApp should be shown in the list of dApps
  • add a button for disconnecting dApp in the wallet
  • disconnect dApp when it's disconnected on dApp side

Screenshots

image image image

All requests are rejected with

{
  "code": -32000,
  "message": "User rejected methods."
}

Checklist

  • Tests that prove my fix is effective or that my feature works have been added
  • Documentation has been added (if appropriate)
  • Screenshots are added (if any UI changes have been made)
  • All TODOs have a corresponding task created (and the link is attached to it)

Copy link

vercel bot commented Sep 27, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
umami-embed-iframe ✅ Ready (Inspect) Visit Preview 💬 Add feedback Dec 1, 2024 10:07am
umami-embed-iframe-ghostnet ✅ Ready (Inspect) Visit Preview 💬 Add feedback Dec 1, 2024 10:07am
umami-v2-web ✅ Ready (Inspect) Visit Preview 💬 Add feedback Dec 1, 2024 10:07am
umami-v2-web-storybook ✅ Ready (Inspect) Visit Preview 💬 Add feedback Dec 1, 2024 10:07am

Copy link
Contributor

@asiia-trilitech asiia-trilitech left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the changes @dianasavvatina

I've added some more comments

apps/web/src/components/WalletConnect/WcProvider.tsx Outdated Show resolved Hide resolved
apps/web/src/components/WalletConnect/WcProvider.tsx Outdated Show resolved Hide resolved
apps/web/src/components/WalletConnect/WcProvider.tsx Outdated Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be good to check existing modals for dApp requests and see what changes we need for the wallet connect implementation (so that designers could start working on that in advance)

This is the latest design for beacon requests that we have: https://www.figma.com/design/l7N0RuVTDuZd2jP77lNqhU/Web-%26-Embed?node-id=6059-103695&node-type=canvas&t=Sc3FdyWv3zI26Sf4-0

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no access to it yet. requested

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's more of a note for the future, for now I think we should keep the current design and focus on the integration bit 🙂

function getChainToConfirm(required: (string | string[] | undefined)[]) {
// take the first required chain which is supported by the wallet
for (const chain of required.flat()) {
if (chain && ["tezos:mainnet", "tezos:ghostnet"].includes(chain)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably use the actual list of networks here by calling useAvailableNetworks()

Also would be better to check by urls not by names - (mainnet & ghostnet are default, but for other networks user can select any name they like)

Could be a TODO for the following changes

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dApp in WalletConnect sends the names of the chains. How can they be selected by url? let's take it next steps

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dianasavvatina does it mean it's always just "tezos:mainnet" and/or "tezos:ghostnet" for tezos? Could it be parisnet for example?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's just tezos:mainnet & tezos:ghostnet - then there is no need for the check (for now, while we only support tezos). These two networks are always defined in Umami and can't be edited by users. We just need to decide on behaviour in the case of request with multiple chains

If there could be other network options we need to figure out how to check if they're present or not.
Names for other (custom) networks are coming from the user and might not match WalletConnect names.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it could be anything, but it must match the config on the wallet.
This is how the proposal looks like:

image

supportedNamespaces: {
tezos: {
chains: [chain],
methods: ["tezos_getAccounts", "tezos_sign", "tezos_send"],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another product question: maybe we can allow user to select which actions they want to allow for the given dApp?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe, but that's a new feature which nobody has requested. I suggest to leave it until somebody explicitly wants it

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree, definitely not needed for this PR

But could be good to discuss it with Aswin and maybe create add a backlog task for later (so that it's not get lost)

<Text as="span" fontWeight="bold">
{name}
</Text>{" "}
<br />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove <br />

export default function VerifyInfobox() {
return (
<Box textAlign="center">
<VStack spacing={4}>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please use real pixel values instead. here and in all the other places

}: Props) {
const form = useFormContext();
return (
<Box as="footer" padding={4}>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we are using pixels (px) everywhere it's better to use px instead of Chakra spacing unit here and in the other places too.
1 spacing unit = 4px

{name}
</Text>{" "}
<br />
<Heading size="md">wants to {intention ? intention : "connect"}</Heading>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

intention ? intention : "connect" could be simplified to intention ?? "connect"

</Link>
</Box>
<Flex alignItems="center" justifyContent="center" marginTop={4}>
<PencilIcon style={{ verticalAlign: "bottom" }} />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<PencilIcon style={{ verticalAlign: "bottom" }} /> is better to replace with => <Icon as={< PencilIcon />} vericalAlign="bottom" /> where Icon is also a component from chakra-ui

disableApprove,
disableReject,
}: IProps) {
const modalContent = useMemo(
Copy link
Contributor

@OKendigelyan OKendigelyan Oct 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useMemo is unnecessary here, so it can be removed, and this component can be simplified to just returning modalContent

}

/**
* Component
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment could be removed

return chain;
}
}
return undefined;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return undefined is the same as return; so explicit undefined here is redundant

}: {
proposal: WalletKitTypes.SessionProposal;
}) {
console.log("SessionProposalModal", proposal);
Copy link
Contributor

@OKendigelyan OKendigelyan Oct 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need console.log here and in other places?

const onReject = () =>
handleAsyncAction(async () => {
try {
setIsLoadingReject(true);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setIsLoadingReject could be moved to finally block after catch inside handleAsyncAction
the same could be applied to the approve logic

namespaces,
sessionProperties: {},
});
onClose();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to call onClose() here since it is called on the line 107
the same thing in the reject logic

import { Card, ModalBody, ModalHeader } from "@chakra-ui/react";
import { Fragment, type ReactNode } from "react";

type IProps = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type IProps => type Props


export const RequestModalContainer = ({ children, title }: IProps) => (
<Fragment>
{title ? (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

{title && (
{title}
)}

children: ReactNode | ReactNode[];
};

export const RequestModalContainer = ({ children, title }: IProps) => (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this component is not needed. inline its only usage

},
},
});
console.debug("approvedNamespaces", namespaces);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please clean these

void initializeWallet();
});

const onSessionProposal = async (event: WalletKitTypes.SessionProposal) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this and the next functions do not provide much value, just inline

<ModalFooter>
<Button
width="100%"
isDisabled={false}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not needed. it's not disabled by default

<Button
width="100%"
isDisabled={!!error || !isValid}
isLoading={isLoadingApprove}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we have separate loading states for those?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants