Skip to content

Commit

Permalink
Add walletconnect window
Browse files Browse the repository at this point in the history
  • Loading branch information
arhtudormorar committed Oct 29, 2024
1 parent c25146b commit 346e061
Show file tree
Hide file tree
Showing 6 changed files with 169 additions and 100 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"@types/qrcode": "^1.5.5",
"isomorphic-fetch": "3.0.0",
"lodash": "4.17.21",
"lit": "3.2.1",
"protobufjs": "7.3.0",
"qrcode": "^1.5.4",
"socket.io-client": "4.7.5",
Expand Down
14 changes: 10 additions & 4 deletions src/core/providers/ProviderFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { createCrossWindowProvider } from './helpers/crossWindow/createCrossWind
import { createExtensionProvider } from './helpers/extension/createExtensionProvider';
import { createMetamaskProvider } from './helpers/iframe/createMetamaskProvider';
import { createWalletconnectProvider } from './helpers/walletconnect/createWalletconnectProvider';
import { networkSelector } from 'store/selectors';
import { getState } from 'store/store';

export class ProviderFactory {
public async create({
Expand All @@ -16,6 +18,7 @@ export class ProviderFactory {
customProvider
}: IProviderFactory): Promise<IProvider | undefined> {
let createdProvider: IProvider | undefined;
const network = networkSelector(getState());

switch (type) {
case ProviderTypeEnum.extension: {
Expand All @@ -30,10 +33,8 @@ export class ProviderFactory {
}

case ProviderTypeEnum.crossWindow: {
const { walletAddress } = config.network;

const provider = await createCrossWindowProvider({
walletAddress,
walletAddress: config.network.walletAddress ?? network.walletAddress,
address: config.account?.address
});
createdProvider = provider as unknown as IProvider;
Expand Down Expand Up @@ -76,7 +77,12 @@ export class ProviderFactory {
}

case ProviderTypeEnum.walletconnect: {
createdProvider = createWalletconnectProvider();
createdProvider = createWalletconnectProvider({
network: {
...network,
...config.network
}
});
break;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { LitElement, html, css } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import QRCode from 'qrcode';

@customElement('wallet-connect-modal')
export class WalletconnectModalComponent extends LitElement {
@property({ type: Boolean }) isOpen = false;
@property({ type: String }) qrCodeData = '';

static styles = css`
.modal {
display: none;
position: fixed;
z-index: 1000;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.4);
}
.modal-content {
background-color: #fefefe;
margin: 15% auto;
padding: 20px;
border: 1px solid #888;
width: 80%;
max-width: 500px;
}
.close {
color: #aaa;
float: right;
font-size: 28px;
font-weight: bold;
cursor: pointer;
}
.close:hover,
.close:focus {
color: black;
text-decoration: none;
cursor: pointer;
}
`;

render() {
return html`
<div class="modal" style="display: ${this.isOpen ? 'block' : 'none'}">
<div class="modal-content">
<span class="close" @click=${this.close}>&times;</span>
<h4>Connect using xPortal on your phone</h4>
<div id="qrContainer"></div>
<button @click=${this.close}>Close</button>
</div>
</div>
`;
}

async open(connectorUri: string) {
this.isOpen = true;
this.qrCodeData = await QRCode.toString(connectorUri, { type: 'svg' });
this.requestUpdate();
await this.updateComplete;
const qrContainer = this.shadowRoot!.getElementById('qrContainer');
if (qrContainer) {
qrContainer.innerHTML = this.qrCodeData;
}
}

close() {
this.isOpen = false;
}
}

export function createModalFunctions() {
const modalElement = document.createElement(
'wallet-connect-modal'
) as WalletconnectModalComponent;
document.body.appendChild(modalElement);

return {
openModal: async (connectorUri: string) => {
await modalElement.open(connectorUri);
},
closeModal: () => {
modalElement.close();
}
};
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,33 @@
import { WalletConnectV2Provider } from '@multiversx/sdk-wallet-connect-provider';
import { IProvider } from 'core/providers/types/providerFactory.types';
import QRCode from 'qrcode';
import { networkSelector } from 'store/selectors';
import { getState } from 'store/store';
import { createModalFunctions } from './components/WalletconnectModalComponent';
import { CurrentNetworkType } from 'types';

interface IWalletconnectProvider {
openModal?: (connectorUri: string) => Promise<void>;
closeModal?: () => void;
onClientLogin?: () => Promise<void>;
onClientLogout?: () => void;
onClientEvent?: (event: any) => void;
network: CurrentNetworkType;
}

export function createWalletconnectProvider(
props: IWalletconnectProvider
): IProvider {
const modalFunctions = createModalFunctions();
const openModal = props.openModal ?? modalFunctions.openModal;
const closeModal = props.closeModal ?? modalFunctions.closeModal;

export function createWalletconnectProvider() {
const network = networkSelector(getState());
const provider = new WalletConnectV2Provider(
prepareCallbacks(),
network.chainId,
network.walletConnectV2RelayAddress,
String(network.walletConnectV2ProjectId)
props.network.chainId,
props.network.walletConnectV2RelayAddress,
String(props.network.walletConnectV2ProjectId)
);

const walletconnectLogin = provider.login;

const createdProvider = provider as unknown as IProvider;

function prepareCallbacks() {
Expand Down Expand Up @@ -42,10 +57,10 @@ export function createWalletconnectProvider() {
throw 'URI not found';
}

await openModal(uri);
await openModal?.(uri);

try {
const account = await provider.login({
const account = await walletconnectLogin({
approval,
token: options?.token
});
Expand All @@ -54,97 +69,17 @@ export function createWalletconnectProvider() {
const signature = account?.signature;

if (!account) {
throw new Error('Connection Proposal Refused');
throw new Error(`Connection Proposal Refused ${account}`);
}

return {
address: address || '',
signature: signature || ''
};
} catch (err) {
throw new Error('Connection Proposal Refused');
throw new Error(`Connection Proposal Refused, ${err}`);
}
};

return createdProvider;
}

async function openModal(connectorUri: string) {
const svg = await QRCode.toString(connectorUri, { type: 'svg' });

// Check if the modal already exists
let modal = document.getElementById('MyWalletConnectV2Modal');

if (!modal) {
// Create the modal HTML
modal = document.createElement('div');
modal.id = 'MyWalletConnectV2Modal';
modal.className = 'modal';

const modalDialog = document.createElement('div');
modalDialog.className = 'modal-dialog';
modal.appendChild(modalDialog);

const modalContent = document.createElement('div');
modalContent.className = 'modal-content';
modalDialog.appendChild(modalContent);

const modalHeader = document.createElement('div');
modalHeader.className = 'modal-header';
modalContent.appendChild(modalHeader);

const modalTitle = document.createElement('h4');
modalTitle.className = 'modal-title';
modalTitle.textContent = 'Connect using xPortal on your phone';
modalHeader.appendChild(modalTitle);

const closeButton = document.createElement('button');
closeButton.type = 'button';
closeButton.className = 'close';
closeButton.textContent = '×';
closeButton.onclick = closeModal;
modalHeader.appendChild(closeButton);

const modalBody = document.createElement('div');
modalBody.className = 'modal-body';
modalContent.appendChild(modalBody);

const qrContainer = document.createElement('div');
qrContainer.id = 'MyWalletConnectV2QRContainer';
modalBody.appendChild(qrContainer);

const modalFooter = document.createElement('div');
modalFooter.className = 'modal-footer';
modalContent.appendChild(modalFooter);

const closeButtonFooter = document.createElement('button');
closeButtonFooter.type = 'button';
closeButtonFooter.className = 'btn btn-danger';
closeButtonFooter.textContent = 'Close';
closeButtonFooter.onclick = closeModal;
modalFooter.appendChild(closeButtonFooter);

// Append the modal to the body
document.body.appendChild(modal);
}

// Get the QR container
const qrContainer = document.getElementById('MyWalletConnectV2QRContainer');

if (qrContainer) {
qrContainer.innerHTML = svg;
}

// Show the modal
modal.classList.add('show');
modal.style.display = 'block';
}

function closeModal() {
const modal = document.getElementById('MyWalletConnectV2Modal');

if (modal) {
modal.classList.remove('show');
modal.style.display = 'none';
}
}
6 changes: 2 additions & 4 deletions src/core/providers/types/providerFactory.types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { IDAppProviderBase } from '@multiversx/sdk-dapp-utils';
import { CustomNetworkType } from 'types/network.types';

// @ts-ignore
export interface IProvider extends IDAppProviderBase {
init: () => Promise<boolean>;
login: (options?: { callbackUrl?: string; token?: string }) => Promise<{
Expand All @@ -20,9 +20,7 @@ export interface IProvider extends IDAppProviderBase {
}

export interface IProviderConfig {
network: {
walletAddress: string;
};
network: CustomNetworkType;
account?: {
address: string;
};
Expand Down
42 changes: 42 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -845,6 +845,18 @@
dependencies:
"@types/node-fetch" "^2.5.10"

"@lit-labs/ssr-dom-shim@^1.2.0":
version "1.2.1"
resolved "https://registry.yarnpkg.com/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.2.1.tgz#2f3a8f1d688935c704dbc89132394a41029acbb8"
integrity sha512-wx4aBmgeGvFmOKucFKY+8VFJSYZxs9poN3SDNQFF6lT6NrQUnHiPB2PWz2sc4ieEcAaYYzN+1uWahEeTq2aRIQ==

"@lit/reactive-element@^2.0.4":
version "2.0.4"
resolved "https://registry.yarnpkg.com/@lit/reactive-element/-/reactive-element-2.0.4.tgz#8f2ed950a848016383894a26180ff06c56ae001b"
integrity sha512-GFn91inaUa2oHLak8awSIigYz0cU0Payr1rcFsrkf5OJ5eSPxElyZfKh0f2p9FsTiZWXQdWGJeXZICEfXXYSXQ==
dependencies:
"@lit-labs/ssr-dom-shim" "^1.2.0"

"@metamask/json-rpc-engine@^7.3.2":
version "7.3.3"
resolved "https://registry.yarnpkg.com/@metamask/json-rpc-engine/-/json-rpc-engine-7.3.3.tgz#f2b30a2164558014bfcca45db10f5af291d989af"
Expand Down Expand Up @@ -1734,6 +1746,11 @@
resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.5.tgz#cb6e2a691b70cb177c6e3ae9c1d2e8b2ea8cd304"
integrity sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==

"@types/trusted-types@^2.0.2":
version "2.0.7"
resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.7.tgz#baccb07a970b91707df3a3e8ba6896c57ead2d11"
integrity sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==

"@types/yargs-parser@*":
version "21.0.3"
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15"
Expand Down Expand Up @@ -5107,6 +5124,31 @@ listhen@^1.7.2:
untun "^0.1.3"
uqr "^0.1.2"

lit-element@^4.1.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lit-element/-/lit-element-4.1.1.tgz#07905992815076e388cf6f1faffc7d6866c82007"
integrity sha512-HO9Tkkh34QkTeUmEdNYhMT8hzLid7YlMlATSi1q4q17HE5d9mrrEHJ/o8O2D0cMi182zK1F3v7x0PWFjrhXFew==
dependencies:
"@lit-labs/ssr-dom-shim" "^1.2.0"
"@lit/reactive-element" "^2.0.4"
lit-html "^3.2.0"

lit-html@^3.2.0:
version "3.2.1"
resolved "https://registry.yarnpkg.com/lit-html/-/lit-html-3.2.1.tgz#8fc49e3531ee5947e4d93e8a5aa642ab1649833b"
integrity sha512-qI/3lziaPMSKsrwlxH/xMgikhQ0EGOX2ICU73Bi/YHFvz2j/yMCIrw4+puF2IpQ4+upd3EWbvnHM9+PnJn48YA==
dependencies:
"@types/trusted-types" "^2.0.2"

lit@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/lit/-/lit-3.2.1.tgz#d6dd15eac20db3a098e81e2c85f70a751ff55592"
integrity sha512-1BBa1E/z0O9ye5fZprPtdqnc0BFzxIxTTOO/tQFmyC/hj1O3jL4TfmLBw0WEwjAokdLwpclkvGgDJwTIh0/22w==
dependencies:
"@lit/reactive-element" "^2.0.4"
lit-element "^4.1.0"
lit-html "^3.2.0"

locate-path@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0"
Expand Down

0 comments on commit 346e061

Please sign in to comment.