From f25fdae40949fea54cb473899085eafeffad1eb7 Mon Sep 17 00:00:00 2001 From: Tudor Morar Date: Mon, 11 Nov 2024 15:02:01 +0200 Subject: [PATCH 01/34] Modal opening --- package.json | 1 + src/core/providers/ProviderFactory.ts | 6 +- .../ledger/components/LedgerModalComponent.ts | 86 +++++++++++++++++++ .../helpers/ledger/createLedgerProvider.ts | 21 ++++- yarn.lock | 42 +++++++++ 5 files changed, 153 insertions(+), 3 deletions(-) create mode 100644 src/core/providers/helpers/ledger/components/LedgerModalComponent.ts diff --git a/package.json b/package.json index 7662de1..f9c9915 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "@multiversx/sdk-web-wallet-provider": "3.2.1", "isomorphic-fetch": "3.0.0", "lodash": "4.17.21", + "lit": "3.2.1", "protobufjs": "7.3.0", "socket.io-client": "4.7.5", "zustand": "4.4.7" diff --git a/src/core/providers/ProviderFactory.ts b/src/core/providers/ProviderFactory.ts index 37a9e9a..346b088 100644 --- a/src/core/providers/ProviderFactory.ts +++ b/src/core/providers/ProviderFactory.ts @@ -18,11 +18,13 @@ export class ProviderFactory { customProvider }: IProviderFactory): Promise { let createdProvider: IProvider | undefined; - const { metamaskSnapWalletAddress = '', walletAddress } = { + const network = { ...networkSelector(getState()), ...config.network }; + const { metamaskSnapWalletAddress = '', walletAddress } = network; + switch (type) { case ProviderTypeEnum.extension: { const provider = await createExtensionProvider(); @@ -46,7 +48,7 @@ export class ProviderFactory { } case ProviderTypeEnum.ledger: { - const ledgerProvider = await createLedgerProvider(); + const ledgerProvider = await createLedgerProvider({ network }); if (!ledgerProvider) { return; diff --git a/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts b/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts new file mode 100644 index 0000000..f51b581 --- /dev/null +++ b/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts @@ -0,0 +1,86 @@ +import { LitElement, html, css } from 'lit'; +import { customElement, property } from 'lit/decorators.js'; + +@customElement('ledger-connect-modal') +export class LedgerConnectModalComponent 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` + + `; + } + + async open() { + this.isOpen = true; + this.qrCodeData = 'asd'; + 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( + 'ledger-connect-modal' + ) as LedgerConnectModalComponent; + document.body.appendChild(modalElement); + + return { + openModal: async () => { + await modalElement.open(); + }, + closeModal: () => { + modalElement.close(); + } + }; +} diff --git a/src/core/providers/helpers/ledger/createLedgerProvider.ts b/src/core/providers/helpers/ledger/createLedgerProvider.ts index d777266..cd7a2b7 100644 --- a/src/core/providers/helpers/ledger/createLedgerProvider.ts +++ b/src/core/providers/helpers/ledger/createLedgerProvider.ts @@ -6,9 +6,23 @@ import { getLedgerProvider } from './getLedgerProvider'; import { fetchAccount } from 'utils/account/fetchAccount'; import { setLedgerLogin } from 'store/actions/loginInfo/loginInfoActions'; import { setLedgerAccount } from 'store/actions/account/accountActions'; +import { createModalFunctions } from './components/LedgerModalComponent'; +import { CurrentNetworkType } from 'types/network.types'; -export async function createLedgerProvider() { +interface ILedgerProvider { + openModal?: () => Promise; + closeModal?: () => void; + network: CurrentNetworkType; +} + +export async function createLedgerProvider( + props: ILedgerProvider +): Promise { const data = await getLedgerProvider(); + const modalFunctions = createModalFunctions(); + + const openModal = props.openModal ?? modalFunctions.openModal; + const closeModal = props.closeModal ?? modalFunctions.closeModal; if (!data) { return null; @@ -29,6 +43,9 @@ export async function createLedgerProvider() { address: string; signature: string; }> => { + console.log('start'); + openModal(); + const isConnected = provider.isConnected(); if (!isConnected) { @@ -73,6 +90,8 @@ export async function createLedgerProvider() { const { version, dataEnabled } = ledgerConfig; + closeModal(); + setLedgerAccount({ address: accountsWithBalance[selectedIndex].address, index: selectedIndex, diff --git a/yarn.lock b/yarn.lock index c47adf8..d4c78cf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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" @@ -1727,6 +1739,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" @@ -5096,6 +5113,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" From be130f5724c2488118b5e9256cd21381ad130a5b Mon Sep 17 00:00:00 2001 From: Tudor Morar Date: Mon, 11 Nov 2024 17:06:01 +0200 Subject: [PATCH 02/34] Modal opening --- .../ledger/components/LedgerModalComponent.ts | 132 +++++++++++------- .../components/ldegerModalComponent.styles.ts | 73 ++++++++++ .../helpers/ledger/createLedgerProvider.ts | 88 ++++++------ .../providers/helpers/ledger/ledger.types.ts | 5 + 4 files changed, 206 insertions(+), 92 deletions(-) create mode 100644 src/core/providers/helpers/ledger/components/ldegerModalComponent.styles.ts create mode 100644 src/core/providers/helpers/ledger/ledger.types.ts diff --git a/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts b/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts index f51b581..2d403e7 100644 --- a/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts +++ b/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts @@ -1,78 +1,109 @@ -import { LitElement, html, css } from 'lit'; +import { LitElement, html } from 'lit'; import { customElement, property } from 'lit/decorators.js'; +import { ledgerStyles } from './ldegerModalComponent.styles'; +import { ILedgerAccount } from '../ledger.types'; +import BigNumber from 'bignumber.js'; -@customElement('ledger-connect-modal') -export class LedgerConnectModalComponent extends LitElement { +@customElement('account-connect-modal') +export class WalletConnectModalComponent extends LitElement { @property({ type: Boolean }) isOpen = false; - @property({ type: String }) qrCodeData = ''; + @property({ type: Array }) accounts: ILedgerAccount[] = []; - 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; - } - `; + static styles = ledgerStyles; render() { return html` `; } + private renderAccounts() { + function trimAddress(s: string): string { + const firstFour = s.slice(0, 4); // Get the first four characters + const lastFour = s.slice(-4); // Get the last four characters + return `${firstFour}...${lastFour}`; // Combine them with three dots in between + } + function formatAmount(amount: string) { + const number = new BigNumber(amount); + const formattedNumber = number + .dividedBy(BigNumber(10).pow(18)) + .toFormat(4) + .toString(); + return formattedNumber; + } + return html` + + ${this.accounts.map( + (account) => html` + + ` + )} + `; + } + async open() { this.isOpen = true; - this.qrCodeData = 'asd'; this.requestUpdate(); - await this.updateComplete; - const qrContainer = this.shadowRoot!.getElementById('qrContainer'); - if (qrContainer) { - qrContainer.innerHTML = this.qrCodeData; - } + } + + updateAccounts(accounts: ILedgerAccount[]) { + this.accounts = accounts; } close() { this.isOpen = false; } + + prevPage() { + // Implement pagination logic + } + + nextPage() { + // Implement pagination logic + } + + accessWallet() { + // Implement wallet access logic + } } export function createModalFunctions() { const modalElement = document.createElement( - 'ledger-connect-modal' - ) as LedgerConnectModalComponent; + 'account-connect-modal' + ) as WalletConnectModalComponent; document.body.appendChild(modalElement); return { @@ -81,6 +112,9 @@ export function createModalFunctions() { }, closeModal: () => { modalElement.close(); + }, + updateLedgerAccounts: (accounts: ILedgerAccount[]) => { + modalElement.updateAccounts(accounts); } }; } diff --git a/src/core/providers/helpers/ledger/components/ldegerModalComponent.styles.ts b/src/core/providers/helpers/ledger/components/ldegerModalComponent.styles.ts new file mode 100644 index 0000000..c5ebc46 --- /dev/null +++ b/src/core/providers/helpers/ledger/components/ldegerModalComponent.styles.ts @@ -0,0 +1,73 @@ +import { css } from 'lit'; + +export const ledgerStyles = 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: #ffffff; + margin: 5% auto; + padding: 20px; + border-radius: 12px; + width: 90%; + max-width: 600px; + } + .modal-header { + text-align: center; + margin-bottom: 20px; + } + .close { + float: right; + cursor: pointer; + padding: 5px; + } + .account-list { + width: 100%; + } + .account-row { + display: flex; + align-items: center; + padding: 12px; + border-radius: 8px; + margin-bottom: 8px; + cursor: pointer; + } + .account-row:hover { + background-color: #f5f5f5; + } + .address { + flex: 2; + font-family: monospace; + } + .balance { + flex: 1; + text-align: right; + } + .index { + flex: 0 0 30px; + text-align: right; + } + .navigation { + display: flex; + justify-content: space-between; + margin-top: 20px; + } + .access-button { + display: block; + width: 200px; + margin: 20px auto; + padding: 12px; + background-color: #1a56db; + color: white; + border: none; + border-radius: 8px; + cursor: pointer; + } +`; diff --git a/src/core/providers/helpers/ledger/createLedgerProvider.ts b/src/core/providers/helpers/ledger/createLedgerProvider.ts index cd7a2b7..bfeae23 100644 --- a/src/core/providers/helpers/ledger/createLedgerProvider.ts +++ b/src/core/providers/helpers/ledger/createLedgerProvider.ts @@ -8,10 +8,12 @@ import { setLedgerLogin } from 'store/actions/loginInfo/loginInfoActions'; import { setLedgerAccount } from 'store/actions/account/accountActions'; import { createModalFunctions } from './components/LedgerModalComponent'; import { CurrentNetworkType } from 'types/network.types'; +import { ILedgerAccount } from './ledger.types'; interface ILedgerProvider { openModal?: () => Promise; closeModal?: () => void; + updateLedgerAccounts?: (wallets: ILedgerAccount[]) => void; network: CurrentNetworkType; } @@ -22,6 +24,8 @@ export async function createLedgerProvider( const modalFunctions = createModalFunctions(); const openModal = props.openModal ?? modalFunctions.openModal; + const updateLedgerAccounts = + props.updateLedgerAccounts ?? modalFunctions.updateLedgerAccounts; const closeModal = props.closeModal ?? modalFunctions.closeModal; if (!data) { @@ -59,11 +63,7 @@ export async function createLedgerProvider( const accounts = await provider.getAccounts(startIndex, addressesPerPage); - const accountsWithBalance: { - address: string; - balance: string; - index: number; - }[] = []; + const accountsWithBalance: ILedgerAccount[] = []; const balancePromises = accounts.map((address) => fetchAccount(address)); @@ -80,45 +80,47 @@ export async function createLedgerProvider( }); }); - // Suppose user selects the first account - const selectedIndex = 0; - - setLedgerLogin({ - index: selectedIndex, - loginType: ProviderTypeEnum.ledger - }); - - const { version, dataEnabled } = ledgerConfig; - - closeModal(); - - setLedgerAccount({ - address: accountsWithBalance[selectedIndex].address, - index: selectedIndex, - version, - hasContractDataEnabled: dataEnabled - }); + updateLedgerAccounts(accountsWithBalance); - if (options?.token) { - const loginInfo = await provider.tokenLogin({ - token: Buffer.from(`${options?.token}{}`), - addressIndex: accountsWithBalance[selectedIndex].index - }); - - return { - address: loginInfo.address, - signature: loginInfo.signature.toString('hex') - }; - } else { - const { address } = await hwProviderLogin({ - addressIndex: accountsWithBalance[selectedIndex].index - }); - - return { - address, - signature: '' - }; - } + // Suppose user selects the first account + // const selectedIndex = 0; + + // setLedgerLogin({ + // index: selectedIndex, + // loginType: ProviderTypeEnum.ledger + // }); + + // const { version, dataEnabled } = ledgerConfig; + + // closeModal(); + + // setLedgerAccount({ + // address: accountsWithBalance[selectedIndex].address, + // index: selectedIndex, + // version, + // hasContractDataEnabled: dataEnabled + // }); + + // if (options?.token) { + // const loginInfo = await provider.tokenLogin({ + // token: Buffer.from(`${options?.token}{}`), + // addressIndex: accountsWithBalance[selectedIndex].index + // }); + + // return { + // address: loginInfo.address, + // signature: loginInfo.signature.toString('hex') + // }; + // } else { + // const { address } = await hwProviderLogin({ + // addressIndex: accountsWithBalance[selectedIndex].index + // }); + + return { + address: options + '', + signature: '' + }; + // } }; return createdProvider; diff --git a/src/core/providers/helpers/ledger/ledger.types.ts b/src/core/providers/helpers/ledger/ledger.types.ts new file mode 100644 index 0000000..7283fd0 --- /dev/null +++ b/src/core/providers/helpers/ledger/ledger.types.ts @@ -0,0 +1,5 @@ +export interface ILedgerAccount { + address: string; + balance: string; + index: number; +} From 62d84e01491f4e7fe79724723004108330a948bf Mon Sep 17 00:00:00 2001 From: Tudor Morar Date: Tue, 12 Nov 2024 12:10:46 +0200 Subject: [PATCH 03/34] Fetching address working --- .../ledger/components/LedgerModalComponent.ts | 57 +++++++++++---- .../helpers/ledger/createLedgerProvider.ts | 70 ++++++++++--------- 2 files changed, 82 insertions(+), 45 deletions(-) diff --git a/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts b/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts index 2d403e7..faf62e8 100644 --- a/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts +++ b/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts @@ -8,6 +8,14 @@ import BigNumber from 'bignumber.js'; export class WalletConnectModalComponent extends LitElement { @property({ type: Boolean }) isOpen = false; @property({ type: Array }) accounts: ILedgerAccount[] = []; + @property({ type: Function }) getAccounts?: (props: { + startIndex: number; + addressesPerPage: number; + }) => Promise; + + // Internal state for pagination + @property({ type: Number }) private startIndex = 0; + @property({ type: Number }) private addressesPerPage = 10; static styles = ledgerStyles; @@ -76,23 +84,41 @@ export class WalletConnectModalComponent extends LitElement { async open() { this.isOpen = true; - this.requestUpdate(); + await this.fetchAccounts(); } - updateAccounts(accounts: ILedgerAccount[]) { - this.accounts = accounts; + private async fetchAccounts() { + if (!this.getAccounts) { + console.error('getAccounts function not provided'); + return; + } + + try { + const accounts = await this.getAccounts({ + startIndex: this.startIndex, + addressesPerPage: this.addressesPerPage + }); + + this.accounts = accounts; + } catch (error) { + console.error('Failed to fetch accounts:', error); + } } - close() { - this.isOpen = false; + async prevPage() { + if (this.startIndex > 0) { + this.startIndex = Math.max(0, this.startIndex - this.addressesPerPage); + await this.fetchAccounts(); + } } - prevPage() { - // Implement pagination logic + async nextPage() { + this.startIndex += this.addressesPerPage; + await this.fetchAccounts(); } - nextPage() { - // Implement pagination logic + close() { + this.isOpen = false; } accessWallet() { @@ -100,10 +126,18 @@ export class WalletConnectModalComponent extends LitElement { } } -export function createModalFunctions() { +export function createModalFunctions(props: { + getAccounts: (props: { + startIndex: number; + addressesPerPage: number; + }) => Promise; +}) { const modalElement = document.createElement( 'account-connect-modal' ) as WalletConnectModalComponent; + + modalElement.getAccounts = props.getAccounts; + document.body.appendChild(modalElement); return { @@ -112,9 +146,6 @@ export function createModalFunctions() { }, closeModal: () => { modalElement.close(); - }, - updateLedgerAccounts: (accounts: ILedgerAccount[]) => { - modalElement.updateAccounts(accounts); } }; } diff --git a/src/core/providers/helpers/ledger/createLedgerProvider.ts b/src/core/providers/helpers/ledger/createLedgerProvider.ts index bfeae23..ec8637f 100644 --- a/src/core/providers/helpers/ledger/createLedgerProvider.ts +++ b/src/core/providers/helpers/ledger/createLedgerProvider.ts @@ -9,11 +9,11 @@ import { setLedgerAccount } from 'store/actions/account/accountActions'; import { createModalFunctions } from './components/LedgerModalComponent'; import { CurrentNetworkType } from 'types/network.types'; import { ILedgerAccount } from './ledger.types'; +import { getAccount } from 'core/methods/account/getAccount'; interface ILedgerProvider { openModal?: () => Promise; closeModal?: () => void; - updateLedgerAccounts?: (wallets: ILedgerAccount[]) => void; network: CurrentNetworkType; } @@ -21,12 +21,6 @@ export async function createLedgerProvider( props: ILedgerProvider ): Promise { const data = await getLedgerProvider(); - const modalFunctions = createModalFunctions(); - - const openModal = props.openModal ?? modalFunctions.openModal; - const updateLedgerAccounts = - props.updateLedgerAccounts ?? modalFunctions.updateLedgerAccounts; - const closeModal = props.closeModal ?? modalFunctions.closeModal; if (!data) { return null; @@ -47,40 +41,52 @@ export async function createLedgerProvider( address: string; signature: string; }> => { - console.log('start'); - openModal(); - const isConnected = provider.isConnected(); if (!isConnected) { throw new Error('Ledger device is not connected'); } - // TODO: perform additional UI logic here - // maybe extract to file - const startIndex = 0; - const addressesPerPage = 10; - - const accounts = await provider.getAccounts(startIndex, addressesPerPage); - - const accountsWithBalance: ILedgerAccount[] = []; - - const balancePromises = accounts.map((address) => fetchAccount(address)); - - const balances = await Promise.all(balancePromises); - - balances.forEach((account, index) => { - if (!account) { - return; + const modalFunctions = createModalFunctions({ + getAccounts: async ({ + startIndex = 0, + addressesPerPage = 10 + }: { + startIndex: number; + addressesPerPage: number; + }) => { + const accounts = await provider.getAccounts( + startIndex, + addressesPerPage + ); + + const accountsWithBalance: ILedgerAccount[] = []; + + const balancePromises = accounts.map((address) => + fetchAccount(address) + ); + + const balances = await Promise.all(balancePromises); + + balances.forEach((account, index) => { + if (!account) { + return; + } + accountsWithBalance.push({ + address: account.address, + balance: account.balance, + index + }); + }); + + return accountsWithBalance; } - accountsWithBalance.push({ - address: account.address, - balance: account.balance, - index - }); }); - updateLedgerAccounts(accountsWithBalance); + const openModal = props.openModal ?? modalFunctions.openModal; + const closeModal = props.closeModal ?? modalFunctions.closeModal; + + openModal(); // Suppose user selects the first account // const selectedIndex = 0; From 26713ef43f259b9480a97e8590abe57a2cc10bfe Mon Sep 17 00:00:00 2001 From: Tudor Morar Date: Tue, 12 Nov 2024 14:47:36 +0200 Subject: [PATCH 04/34] get accounts working --- .../ledger/components/LedgerModalComponent.ts | 40 ++++++++++++++++-- .../helpers/ledger/createLedgerProvider.ts | 42 ++++--------------- 2 files changed, 46 insertions(+), 36 deletions(-) diff --git a/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts b/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts index faf62e8..64006e9 100644 --- a/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts +++ b/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts @@ -3,6 +3,7 @@ import { customElement, property } from 'lit/decorators.js'; import { ledgerStyles } from './ldegerModalComponent.styles'; import { ILedgerAccount } from '../ledger.types'; import BigNumber from 'bignumber.js'; +import { fetchAccount } from 'utils/account/fetchAccount'; @customElement('account-connect-modal') export class WalletConnectModalComponent extends LitElement { @@ -127,16 +128,49 @@ export class WalletConnectModalComponent extends LitElement { } export function createModalFunctions(props: { - getAccounts: (props: { + getAccounts: ({ + startIndex, + addressesPerPage + }: { startIndex: number; addressesPerPage: number; - }) => Promise; + }) => Promise; }) { const modalElement = document.createElement( 'account-connect-modal' ) as WalletConnectModalComponent; - modalElement.getAccounts = props.getAccounts; + modalElement.getAccounts = async ({ + startIndex = 0, + addressesPerPage = 10 + }: { + startIndex: number; + addressesPerPage: number; + }) => { + const accounts = await props.getAccounts({ + startIndex, + addressesPerPage + }); + + const accountsWithBalance: ILedgerAccount[] = []; + + const balancePromises = accounts.map((address) => fetchAccount(address)); + + const balances = await Promise.all(balancePromises); + + balances.forEach((account, index) => { + if (!account) { + return; + } + accountsWithBalance.push({ + address: account.address, + balance: account.balance, + index + }); + }); + + return accountsWithBalance; + }; document.body.appendChild(modalElement); diff --git a/src/core/providers/helpers/ledger/createLedgerProvider.ts b/src/core/providers/helpers/ledger/createLedgerProvider.ts index ec8637f..68af7fc 100644 --- a/src/core/providers/helpers/ledger/createLedgerProvider.ts +++ b/src/core/providers/helpers/ledger/createLedgerProvider.ts @@ -47,40 +47,16 @@ export async function createLedgerProvider( throw new Error('Ledger device is not connected'); } + const getAccounts = async ({ + startIndex = 0, + addressesPerPage = 10 + }: { + startIndex: number; + addressesPerPage: number; + }) => await provider.getAccounts(startIndex, addressesPerPage); + const modalFunctions = createModalFunctions({ - getAccounts: async ({ - startIndex = 0, - addressesPerPage = 10 - }: { - startIndex: number; - addressesPerPage: number; - }) => { - const accounts = await provider.getAccounts( - startIndex, - addressesPerPage - ); - - const accountsWithBalance: ILedgerAccount[] = []; - - const balancePromises = accounts.map((address) => - fetchAccount(address) - ); - - const balances = await Promise.all(balancePromises); - - balances.forEach((account, index) => { - if (!account) { - return; - } - accountsWithBalance.push({ - address: account.address, - balance: account.balance, - index - }); - }); - - return accountsWithBalance; - } + getAccounts }); const openModal = props.openModal ?? modalFunctions.openModal; From b8da059b0ef60587384a5e38345cddaaa72c1717 Mon Sep 17 00:00:00 2001 From: Tudor Morar Date: Tue, 12 Nov 2024 15:18:01 +0200 Subject: [PATCH 05/34] get accounts working --- .../ledger/components/LedgerModalComponent.ts | 62 ++++++++----------- 1 file changed, 27 insertions(+), 35 deletions(-) diff --git a/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts b/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts index 64006e9..ab96bec 100644 --- a/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts +++ b/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts @@ -5,14 +5,19 @@ import { ILedgerAccount } from '../ledger.types'; import BigNumber from 'bignumber.js'; import { fetchAccount } from 'utils/account/fetchAccount'; +console.log('\x1b[42m%s\x1b[0m', 'link:', 1); + @customElement('account-connect-modal') export class WalletConnectModalComponent extends LitElement { @property({ type: Boolean }) isOpen = false; @property({ type: Array }) accounts: ILedgerAccount[] = []; - @property({ type: Function }) getAccounts?: (props: { + @property({ type: Function }) getAccounts?: ({ + startIndex, + addressesPerPage + }: { startIndex: number; addressesPerPage: number; - }) => Promise; + }) => Promise; // Internal state for pagination @property({ type: Number }) private startIndex = 0; @@ -95,12 +100,29 @@ export class WalletConnectModalComponent extends LitElement { } try { - const accounts = await this.getAccounts({ + const accounts = await this.getAccounts?.({ startIndex: this.startIndex, addressesPerPage: this.addressesPerPage }); - this.accounts = accounts; + const accountsWithBalance: ILedgerAccount[] = []; + + const balancePromises = accounts.map((address) => fetchAccount(address)); + + const balances = await Promise.all(balancePromises); + + balances.forEach((account, index) => { + if (!account) { + return; + } + accountsWithBalance.push({ + address: account.address, + balance: account.balance, + index + }); + }); + + this.accounts = accountsWithBalance; } catch (error) { console.error('Failed to fetch accounts:', error); } @@ -140,37 +162,7 @@ export function createModalFunctions(props: { 'account-connect-modal' ) as WalletConnectModalComponent; - modalElement.getAccounts = async ({ - startIndex = 0, - addressesPerPage = 10 - }: { - startIndex: number; - addressesPerPage: number; - }) => { - const accounts = await props.getAccounts({ - startIndex, - addressesPerPage - }); - - const accountsWithBalance: ILedgerAccount[] = []; - - const balancePromises = accounts.map((address) => fetchAccount(address)); - - const balances = await Promise.all(balancePromises); - - balances.forEach((account, index) => { - if (!account) { - return; - } - accountsWithBalance.push({ - address: account.address, - balance: account.balance, - index - }); - }); - - return accountsWithBalance; - }; + modalElement.getAccounts = props.getAccounts; document.body.appendChild(modalElement); From 6a423b680af24149adb296ac12d0e23612a61961 Mon Sep 17 00:00:00 2001 From: Tudor Morar Date: Tue, 12 Nov 2024 15:23:22 +0200 Subject: [PATCH 06/34] get accounts working --- .../ledger/components/LedgerModalComponent.ts | 27 +++++++------------ .../helpers/ledger/createLedgerProvider.ts | 11 +++----- 2 files changed, 13 insertions(+), 25 deletions(-) diff --git a/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts b/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts index ab96bec..1bf3ca2 100644 --- a/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts +++ b/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts @@ -11,13 +11,10 @@ console.log('\x1b[42m%s\x1b[0m', 'link:', 1); export class WalletConnectModalComponent extends LitElement { @property({ type: Boolean }) isOpen = false; @property({ type: Array }) accounts: ILedgerAccount[] = []; - @property({ type: Function }) getAccounts?: ({ - startIndex, - addressesPerPage - }: { - startIndex: number; - addressesPerPage: number; - }) => Promise; + @property({ type: Function }) getAccounts?: ( + page?: number, + pageSize?: number + ) => Promise; // Internal state for pagination @property({ type: Number }) private startIndex = 0; @@ -100,10 +97,10 @@ export class WalletConnectModalComponent extends LitElement { } try { - const accounts = await this.getAccounts?.({ - startIndex: this.startIndex, - addressesPerPage: this.addressesPerPage - }); + const accounts = await this.getAccounts?.( + this.startIndex, + this.addressesPerPage + ); const accountsWithBalance: ILedgerAccount[] = []; @@ -150,13 +147,7 @@ export class WalletConnectModalComponent extends LitElement { } export function createModalFunctions(props: { - getAccounts: ({ - startIndex, - addressesPerPage - }: { - startIndex: number; - addressesPerPage: number; - }) => Promise; + getAccounts: (page?: number, pageSize?: number) => Promise; }) { const modalElement = document.createElement( 'account-connect-modal' diff --git a/src/core/providers/helpers/ledger/createLedgerProvider.ts b/src/core/providers/helpers/ledger/createLedgerProvider.ts index 68af7fc..7b41316 100644 --- a/src/core/providers/helpers/ledger/createLedgerProvider.ts +++ b/src/core/providers/helpers/ledger/createLedgerProvider.ts @@ -47,13 +47,10 @@ export async function createLedgerProvider( throw new Error('Ledger device is not connected'); } - const getAccounts = async ({ - startIndex = 0, - addressesPerPage = 10 - }: { - startIndex: number; - addressesPerPage: number; - }) => await provider.getAccounts(startIndex, addressesPerPage); + const getAccounts: typeof provider.getAccounts = async ( + startIndex, + addressesPerPage + ) => await provider.getAccounts(startIndex, addressesPerPage); const modalFunctions = createModalFunctions({ getAccounts From d05f7d267d08cb6d4e1808b164197d76e4bbc617 Mon Sep 17 00:00:00 2001 From: Tudor Morar Date: Tue, 12 Nov 2024 15:24:37 +0200 Subject: [PATCH 07/34] remove log --- .../providers/helpers/ledger/components/LedgerModalComponent.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts b/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts index 1bf3ca2..fc2808d 100644 --- a/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts +++ b/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts @@ -5,8 +5,6 @@ import { ILedgerAccount } from '../ledger.types'; import BigNumber from 'bignumber.js'; import { fetchAccount } from 'utils/account/fetchAccount'; -console.log('\x1b[42m%s\x1b[0m', 'link:', 1); - @customElement('account-connect-modal') export class WalletConnectModalComponent extends LitElement { @property({ type: Boolean }) isOpen = false; From 363010f4f562bb828b4cd4cf09468863779c45b0 Mon Sep 17 00:00:00 2001 From: Tudor Morar Date: Tue, 12 Nov 2024 16:29:47 +0200 Subject: [PATCH 08/34] get accounts working --- .../helpers/ledger/components/LedgerModalComponent.ts | 4 ++-- src/core/providers/helpers/ledger/createLedgerProvider.ts | 7 +------ 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts b/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts index fc2808d..3db4e5f 100644 --- a/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts +++ b/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts @@ -156,8 +156,8 @@ export function createModalFunctions(props: { document.body.appendChild(modalElement); return { - openModal: async () => { - await modalElement.open(); + openModal: () => { + modalElement.open(); }, closeModal: () => { modalElement.close(); diff --git a/src/core/providers/helpers/ledger/createLedgerProvider.ts b/src/core/providers/helpers/ledger/createLedgerProvider.ts index 7b41316..182489c 100644 --- a/src/core/providers/helpers/ledger/createLedgerProvider.ts +++ b/src/core/providers/helpers/ledger/createLedgerProvider.ts @@ -47,13 +47,8 @@ export async function createLedgerProvider( throw new Error('Ledger device is not connected'); } - const getAccounts: typeof provider.getAccounts = async ( - startIndex, - addressesPerPage - ) => await provider.getAccounts(startIndex, addressesPerPage); - const modalFunctions = createModalFunctions({ - getAccounts + getAccounts: provider.getAccounts.bind(provider) }); const openModal = props.openModal ?? modalFunctions.openModal; From af7fe5c0cee9132910de0d9672c5ffe3fccab526 Mon Sep 17 00:00:00 2001 From: Tudor Morar Date: Tue, 12 Nov 2024 16:52:19 +0200 Subject: [PATCH 09/34] Pagination working --- .../ledger/components/LedgerModalComponent.ts | 64 ++++++++++++------- 1 file changed, 41 insertions(+), 23 deletions(-) diff --git a/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts b/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts index 3db4e5f..54993b3 100644 --- a/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts +++ b/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts @@ -65,27 +65,29 @@ export class WalletConnectModalComponent extends LitElement { Balance # - ${this.accounts.map( - (account) => html` - - ` - )} + ${this.accounts + .slice(this.startIndex, this.startIndex + this.addressesPerPage) + .map( + (account) => html` + + ` + )} `; } async open() { + this.fetchAccounts(); this.isOpen = true; - await this.fetchAccounts(); } private async fetchAccounts() { @@ -94,13 +96,32 @@ export class WalletConnectModalComponent extends LitElement { return; } + const hasData = this.accounts.some( + ({ index, balance }) => index === this.startIndex && balance !== '...' + ); + + if (hasData) { + return; + } + try { const accounts = await this.getAccounts?.( this.startIndex, this.addressesPerPage ); - const accountsWithBalance: ILedgerAccount[] = []; + const accountsWithBalance: ILedgerAccount[] = accounts.map( + (address, index) => { + return { + address, + balance: '...', + index: index + this.startIndex + }; + } + ); + this.accounts = [...this.accounts, ...accountsWithBalance]; + + this.requestUpdate(); const balancePromises = accounts.map((address) => fetchAccount(address)); @@ -110,14 +131,11 @@ export class WalletConnectModalComponent extends LitElement { if (!account) { return; } - accountsWithBalance.push({ - address: account.address, - balance: account.balance, - index - }); + const accountArrayIndex = index + this.startIndex; + this.accounts[accountArrayIndex].balance = account.balance; }); - this.accounts = accountsWithBalance; + this.requestUpdate(); } catch (error) { console.error('Failed to fetch accounts:', error); } From 2a14d6317bfdc14837c634da6b9f2252b0c9b5cd Mon Sep 17 00:00:00 2001 From: Tudor Morar Date: Tue, 12 Nov 2024 17:04:52 +0200 Subject: [PATCH 10/34] Show spinner while addresses are loading --- .../ledger/components/LedgerModalComponent.ts | 27 +++++++++++++++---- .../components/ldegerModalComponent.styles.ts | 26 ++++++++++++++++++ 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts b/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts index 54993b3..d8936aa 100644 --- a/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts +++ b/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts @@ -17,6 +17,7 @@ export class WalletConnectModalComponent extends LitElement { // Internal state for pagination @property({ type: Number }) private startIndex = 0; @property({ type: Number }) private addressesPerPage = 10; + @property({ type: Boolean }) private isLoading = false; static styles = ledgerStyles; @@ -30,7 +31,11 @@ export class WalletConnectModalComponent extends LitElement {

Choose the wallet you want to access

- + @@ -150,8 +152,6 @@ export class WalletConnectModalComponent extends LitElement { const accountArrayIndex = index + this.startIndex; this.accounts[accountArrayIndex].balance = account.balance; }); - - this.requestUpdate(); } catch (error) { this.isLoading = false; console.error('Failed to fetch accounts:', error); @@ -162,12 +162,14 @@ export class WalletConnectModalComponent extends LitElement { if (this.startIndex > 0) { this.startIndex = Math.max(0, this.startIndex - this.addressesPerPage); await this.fetchAccounts(); + this.requestUpdate(); } } async nextPage() { this.startIndex += this.addressesPerPage; await this.fetchAccounts(); + this.requestUpdate(); } close() { From 7ad3f21966192e1393ef1963e3a72a60e09328a7 Mon Sep 17 00:00:00 2001 From: Tudor Morar Date: Wed, 13 Nov 2024 11:25:31 +0200 Subject: [PATCH 12/34] Fix request update --- .../helpers/ledger/components/LedgerModalComponent.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts b/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts index 2145d77..8ca2921 100644 --- a/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts +++ b/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts @@ -152,6 +152,7 @@ export class WalletConnectModalComponent extends LitElement { const accountArrayIndex = index + this.startIndex; this.accounts[accountArrayIndex].balance = account.balance; }); + this.requestUpdate(); } catch (error) { this.isLoading = false; console.error('Failed to fetch accounts:', error); @@ -162,14 +163,12 @@ export class WalletConnectModalComponent extends LitElement { if (this.startIndex > 0) { this.startIndex = Math.max(0, this.startIndex - this.addressesPerPage); await this.fetchAccounts(); - this.requestUpdate(); } } async nextPage() { this.startIndex += this.addressesPerPage; await this.fetchAccounts(); - this.requestUpdate(); } close() { From 6dd1fea2b81aa3f511107be9656abd9b1e62038c Mon Sep 17 00:00:00 2001 From: Tudor Morar Date: Wed, 13 Nov 2024 11:51:35 +0200 Subject: [PATCH 13/34] Make access button disabled conditionally --- .../ledger/components/LedgerModalComponent.ts | 63 ++++++++++++------- .../components/ldegerModalComponent.styles.ts | 8 +++ 2 files changed, 49 insertions(+), 22 deletions(-) diff --git a/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts b/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts index 8ca2921..95aef69 100644 --- a/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts +++ b/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts @@ -4,6 +4,7 @@ import { ledgerStyles } from './ldegerModalComponent.styles'; import { ILedgerAccount } from '../ledger.types'; import BigNumber from 'bignumber.js'; import { fetchAccount } from 'utils/account/fetchAccount'; +import { c } from 'msw/lib/glossary-de6278a9'; @customElement('account-connect-modal') export class WalletConnectModalComponent extends LitElement { @@ -14,14 +15,22 @@ export class WalletConnectModalComponent extends LitElement { pageSize?: number ) => Promise; - // Internal state for pagination @property({ type: Number }) private startIndex = 0; @property({ type: Number }) private addressesPerPage = 10; @property({ type: Boolean }) private isLoading = false; + @property({ type: Number }) private selectedIndex = 0; static styles = ledgerStyles; render() { + const shownAccounts = this.accounts.slice( + this.startIndex, + this.startIndex + this.addressesPerPage + ); + const isSelectedIndexOnPage = shownAccounts.some( + ({ index }) => index === this.selectedIndex + ); + return html` - ${this.accounts - .slice(this.startIndex, this.startIndex + this.addressesPerPage) - .map( - (account) => html` - - ` - )} + ${shownAccounts.map( + (account) => html` + + ` + )} `; } + private selectAccount(index: number) { + this.selectedIndex = index; + this.requestUpdate(); + } + async open() { this.fetchAccounts(); this.isOpen = true; @@ -176,7 +195,7 @@ export class WalletConnectModalComponent extends LitElement { } accessWallet() { - // Implement wallet access logic + console.log('Accessing wallet with index:', this.selectedIndex); } } diff --git a/src/core/providers/helpers/ledger/components/ldegerModalComponent.styles.ts b/src/core/providers/helpers/ledger/components/ldegerModalComponent.styles.ts index b2d6e0f..fbd7b9a 100644 --- a/src/core/providers/helpers/ledger/components/ldegerModalComponent.styles.ts +++ b/src/core/providers/helpers/ledger/components/ldegerModalComponent.styles.ts @@ -72,6 +72,14 @@ export const ledgerStyles = css` border-radius: 8px; cursor: pointer; } + .access-button:disabled { + background-color: #a1a1a1; /* Muted gray */ + color: #e0e0e0; /* Lighter text color */ + cursor: not-allowed; + opacity: 0.6; + box-shadow: none; + transform: none; + } .spinner { display: flex; justify-content: center; From 50ff5a2873fac7de66833a9d8693844b30cfa358 Mon Sep 17 00:00:00 2001 From: Tudor Morar Date: Wed, 13 Nov 2024 17:51:56 +0200 Subject: [PATCH 14/34] Login working --- .../ledger/components/LedgerModalComponent.ts | 40 +++++---- .../helpers/ledger/createLedgerProvider.ts | 88 ++++++++----------- 2 files changed, 63 insertions(+), 65 deletions(-) diff --git a/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts b/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts index 95aef69..3aefa74 100644 --- a/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts +++ b/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts @@ -4,7 +4,6 @@ import { ledgerStyles } from './ldegerModalComponent.styles'; import { ILedgerAccount } from '../ledger.types'; import BigNumber from 'bignumber.js'; import { fetchAccount } from 'utils/account/fetchAccount'; -import { c } from 'msw/lib/glossary-de6278a9'; @customElement('account-connect-modal') export class WalletConnectModalComponent extends LitElement { @@ -14,11 +13,13 @@ export class WalletConnectModalComponent extends LitElement { page?: number, pageSize?: number ) => Promise; + @property({ type: Function }) accessWallet?: () => void; @property({ type: Number }) private startIndex = 0; @property({ type: Number }) private addressesPerPage = 10; @property({ type: Boolean }) private isLoading = false; - @property({ type: Number }) private selectedIndex = 0; + @property({ type: Number }) public selectedIndex = 0; + @property({ type: String }) public selectedAddress = ''; static styles = ledgerStyles; @@ -104,7 +105,7 @@ export class WalletConnectModalComponent extends LitElement { value=${account.index} /> ${trimAddress(account.address)} - ${formatAmount(account.balance)} + ${formatAmount(account.balance ?? '')} ${account.index} ` @@ -114,6 +115,10 @@ export class WalletConnectModalComponent extends LitElement { private selectAccount(index: number) { this.selectedIndex = index; + + this.selectedAddress = + this.accounts.find((account) => account.index === index)?.address ?? ''; + this.requestUpdate(); } @@ -193,13 +198,9 @@ export class WalletConnectModalComponent extends LitElement { close() { this.isOpen = false; } - - accessWallet() { - console.log('Accessing wallet with index:', this.selectedIndex); - } } -export function createModalFunctions(props: { +export async function createModalFunctions(props: { getAccounts: (page?: number, pageSize?: number) => Promise; }) { const modalElement = document.createElement( @@ -209,13 +210,22 @@ export function createModalFunctions(props: { modalElement.getAccounts = props.getAccounts; document.body.appendChild(modalElement); + modalElement.open(); - return { - openModal: () => { - modalElement.open(); - }, - closeModal: () => { - modalElement.close(); + const selectedAccount = await new Promise<{ address: string; index: number }>( + (resolve, reject) => { + modalElement.accessWallet = () => { + if (!modalElement.selectedAddress) { + return reject('No address selected'); + } + resolve({ + address: modalElement.selectedAddress, + index: modalElement.selectedIndex + }); + document.body.removeChild(modalElement); + }; } - }; + ); + + return selectedAccount; } diff --git a/src/core/providers/helpers/ledger/createLedgerProvider.ts b/src/core/providers/helpers/ledger/createLedgerProvider.ts index 182489c..a2cda6a 100644 --- a/src/core/providers/helpers/ledger/createLedgerProvider.ts +++ b/src/core/providers/helpers/ledger/createLedgerProvider.ts @@ -3,25 +3,23 @@ import { ProviderTypeEnum } from 'core/providers/types/providerFactory.types'; import { getLedgerProvider } from './getLedgerProvider'; -import { fetchAccount } from 'utils/account/fetchAccount'; import { setLedgerLogin } from 'store/actions/loginInfo/loginInfoActions'; import { setLedgerAccount } from 'store/actions/account/accountActions'; import { createModalFunctions } from './components/LedgerModalComponent'; import { CurrentNetworkType } from 'types/network.types'; -import { ILedgerAccount } from './ledger.types'; -import { getAccount } from 'core/methods/account/getAccount'; interface ILedgerProvider { openModal?: () => Promise; - closeModal?: () => void; network: CurrentNetworkType; } export async function createLedgerProvider( - props: ILedgerProvider + props?: ILedgerProvider ): Promise { const data = await getLedgerProvider(); + console.log('props', props); + if (!data) { return null; } @@ -47,54 +45,44 @@ export async function createLedgerProvider( throw new Error('Ledger device is not connected'); } - const modalFunctions = createModalFunctions({ + const { address, index: selectedIndex } = await createModalFunctions({ getAccounts: provider.getAccounts.bind(provider) }); - const openModal = props.openModal ?? modalFunctions.openModal; - const closeModal = props.closeModal ?? modalFunctions.closeModal; - - openModal(); - - // Suppose user selects the first account - // const selectedIndex = 0; - - // setLedgerLogin({ - // index: selectedIndex, - // loginType: ProviderTypeEnum.ledger - // }); - - // const { version, dataEnabled } = ledgerConfig; - - // closeModal(); - - // setLedgerAccount({ - // address: accountsWithBalance[selectedIndex].address, - // index: selectedIndex, - // version, - // hasContractDataEnabled: dataEnabled - // }); - - // if (options?.token) { - // const loginInfo = await provider.tokenLogin({ - // token: Buffer.from(`${options?.token}{}`), - // addressIndex: accountsWithBalance[selectedIndex].index - // }); - - // return { - // address: loginInfo.address, - // signature: loginInfo.signature.toString('hex') - // }; - // } else { - // const { address } = await hwProviderLogin({ - // addressIndex: accountsWithBalance[selectedIndex].index - // }); - - return { - address: options + '', - signature: '' - }; - // } + setLedgerLogin({ + index: selectedIndex, + loginType: ProviderTypeEnum.ledger + }); + + const { version, dataEnabled } = ledgerConfig; + + setLedgerAccount({ + address, + index: selectedIndex, + version, + hasContractDataEnabled: dataEnabled + }); + + if (options?.token) { + const loginInfo = await provider.tokenLogin({ + token: Buffer.from(`${options?.token}{}`), + addressIndex: selectedIndex + }); + + return { + address: loginInfo.address, + signature: loginInfo.signature.toString('hex') + }; + } else { + const { address } = await hwProviderLogin({ + addressIndex: selectedIndex + }); + + return { + address, + signature: '' + }; + } }; return createdProvider; From 63245f1bf8b039ee8190acd63232ad0894114849 Mon Sep 17 00:00:00 2001 From: Tudor Morar Date: Thu, 14 Nov 2024 11:58:34 +0200 Subject: [PATCH 15/34] Ledger logging in --- src/constants/index.ts | 1 + src/constants/ledger.constants.ts | 6 + .../ledger/components/LedgerModalComponent.ts | 172 ++++++++++++++---- .../ledger/components/getAuthTokenText.ts | 45 +++++ .../components/getLedgerVersionOptions.ts | 67 +++++++ .../ledger/components/secondsToTimeString.ts | 29 +++ .../tests/getLedgerVersionOptions.test.ts | 84 +++++++++ .../tests/secondsToTimeString.test.ts | 51 ++++++ .../helpers/ledger/createLedgerProvider.ts | 68 ++++--- 9 files changed, 459 insertions(+), 64 deletions(-) create mode 100644 src/constants/ledger.constants.ts create mode 100644 src/core/providers/helpers/ledger/components/getAuthTokenText.ts create mode 100644 src/core/providers/helpers/ledger/components/getLedgerVersionOptions.ts create mode 100644 src/core/providers/helpers/ledger/components/secondsToTimeString.ts create mode 100644 src/core/providers/helpers/ledger/components/tests/getLedgerVersionOptions.test.ts create mode 100644 src/core/providers/helpers/ledger/components/tests/secondsToTimeString.test.ts diff --git a/src/constants/index.ts b/src/constants/index.ts index 6047bdd..f5f3b16 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -5,3 +5,4 @@ export * from './window.constants'; export * from './browser.constants'; export * from './errorMessages.constants'; export * from './mvx.constants'; +export * from './ledger.constants'; diff --git a/src/constants/ledger.constants.ts b/src/constants/ledger.constants.ts new file mode 100644 index 0000000..3c38b38 --- /dev/null +++ b/src/constants/ledger.constants.ts @@ -0,0 +1,6 @@ +export const LEDGER_MULTI_ACCOUNT_MINIMUM_VERSION = '1.0.8'; +export const LEDGER_HASH_SIGN_MINIMUM_VERSION = '1.0.11'; +export const LEDGER_SIGN_AUTH_TOKEN_MINIMUM_VERSION = '1.0.15'; +export const LEDGER_WITH_WHITELISTED_TOKENS_MINIMUM_VERSION = '1.0.17'; +export const LEDGER_WITH_GUARDIANS_MINIMUM_VERSION = '1.0.22'; +export const LEDGER_WITH_USERNAMES_MINIMUM_VERSION = '1.0.23'; diff --git a/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts b/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts index 3aefa74..efa258b 100644 --- a/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts +++ b/src/core/providers/helpers/ledger/components/LedgerModalComponent.ts @@ -1,25 +1,37 @@ -import { LitElement, html } from 'lit'; +import { LitElement, TemplateResult, html } from 'lit'; import { customElement, property } from 'lit/decorators.js'; import { ledgerStyles } from './ldegerModalComponent.styles'; import { ILedgerAccount } from '../ledger.types'; import BigNumber from 'bignumber.js'; import { fetchAccount } from 'utils/account/fetchAccount'; +import { getAuthTokenText } from './getAuthTokenText'; @customElement('account-connect-modal') export class WalletConnectModalComponent extends LitElement { @property({ type: Boolean }) isOpen = false; @property({ type: Array }) accounts: ILedgerAccount[] = []; + // external props + @property({ type: String }) loginToken = ''; + @property({ type: String }) version = ''; @property({ type: Function }) getAccounts?: ( page?: number, pageSize?: number ) => Promise; - @property({ type: Function }) accessWallet?: () => void; + @property({ type: Function }) onSubmit?: (props: { + addressIndex: number; + }) => Promise<{ + address: string; + addressIndex: number; + signature?: string; + }>; @property({ type: Number }) private startIndex = 0; @property({ type: Number }) private addressesPerPage = 10; @property({ type: Boolean }) private isLoading = false; + @property({ type: Boolean }) private showConfirm = false; @property({ type: Number }) public selectedIndex = 0; @property({ type: String }) public selectedAddress = ''; + @property({ type: String }) public signature = ''; static styles = ledgerStyles; @@ -32,35 +44,108 @@ export class WalletConnectModalComponent extends LitElement { ({ index }) => index === this.selectedIndex ); - return html` -