From 003416e66450f61cdefc7dd018a0aa10cbf34652 Mon Sep 17 00:00:00 2001 From: Anton Lykhoyda Date: Tue, 30 Jan 2024 16:48:38 +0100 Subject: [PATCH 1/2] feat: network selector component (#70) Closes: #64 !!!! To clarify: "filter source/dest network on network selection (inside WidgetController)" --- packages/widget/src/assets/icons/chevron.ts | 19 +++ packages/widget/src/assets/index.ts | 44 +++++-- .../network-selector/network-selector.ts | 123 +++++++++++------- .../src/components/network-selector/styles.ts | 96 ++++++++++++-- packages/widget/src/styles.ts | 4 + packages/widget/src/widget.ts | 2 +- .../network-selector/network-selector.test.ts | 86 ++++++++---- 7 files changed, 281 insertions(+), 93 deletions(-) create mode 100644 packages/widget/src/assets/icons/chevron.ts diff --git a/packages/widget/src/assets/icons/chevron.ts b/packages/widget/src/assets/icons/chevron.ts new file mode 100644 index 00000000..ab4b01bb --- /dev/null +++ b/packages/widget/src/assets/icons/chevron.ts @@ -0,0 +1,19 @@ +import { html } from 'lit'; + +const chevronIcon = html` + + `; + +export default chevronIcon; diff --git a/packages/widget/src/assets/index.ts b/packages/widget/src/assets/index.ts index cb41b5ca..8069197f 100644 --- a/packages/widget/src/assets/index.ts +++ b/packages/widget/src/assets/index.ts @@ -1,9 +1,35 @@ -export { default as switchNetworkIcon } from './icons/switchNetwork'; -export { default as sygmaLogo } from './icons/sygmaLogo'; -export { default as noNetworkIcon } from './icons/noNetworkIcon'; -export { default as ethereumIcon } from './icons/ethereumNetworkIcon'; -export { default as polygonNetworkIcon } from './icons/polygonNetworkIcon'; -export { default as baseNetworkIcon } from './icons/baseNetworkIcon'; -export { default as cronosNetworkIcon } from './icons/cronosNetworkIcon'; -export { default as phalaNetworkIcon } from './icons/phalaNetworkIcon'; -export { default as khalaNetworkIcon } from './icons/khalaNetworkIcon'; +import type { HTMLTemplateResult } from 'lit'; +import baseNetworkIcon from './icons/baseNetworkIcon'; +import cronosNetworkIcon from './icons/cronosNetworkIcon'; +import ethereumIcon from './icons/ethereumNetworkIcon'; +import khalaNetworkIcon from './icons/khalaNetworkIcon'; +import noNetworkIcon from './icons/noNetworkIcon'; +import phalaNetworkIcon from './icons/phalaNetworkIcon'; +import polygonNetworkIcon from './icons/polygonNetworkIcon'; +import switchNetworkIcon from './icons/switchNetwork'; +import sygmaLogo from './icons/sygmaLogo'; +import chevronIcon from './icons/chevron'; + +export const networkIconsMap = { + ethereum: ethereumIcon, + khala: khalaNetworkIcon, + phala: phalaNetworkIcon, + cronos: cronosNetworkIcon, + base: baseNetworkIcon, + gnosis: noNetworkIcon, + polygon: polygonNetworkIcon, + default: noNetworkIcon +} as const as Record; + +export { + ethereumIcon, + khalaNetworkIcon, + phalaNetworkIcon, + cronosNetworkIcon, + baseNetworkIcon, + polygonNetworkIcon, + sygmaLogo, + switchNetworkIcon, + noNetworkIcon, + chevronIcon +}; diff --git a/packages/widget/src/components/network-selector/network-selector.ts b/packages/widget/src/components/network-selector/network-selector.ts index 21f18f42..3cec4355 100644 --- a/packages/widget/src/components/network-selector/network-selector.ts +++ b/packages/widget/src/components/network-selector/network-selector.ts @@ -1,8 +1,11 @@ import type { Domain } from '@buildwithsygma/sygma-sdk-core'; -import { LitElement, html, type HTMLTemplateResult } from 'lit'; -import { customElement, property } from 'lit/decorators.js'; +import { LitElement, html } from 'lit'; +import type { HTMLTemplateResult } from 'lit'; +import { customElement, property, state } from 'lit/decorators.js'; import { map } from 'lit/directives/map.js'; +import { when } from 'lit/directives/when.js'; import { capitalize } from '../../utils'; +import { networkIconsMap, chevronIcon } from '../../assets'; import { styles } from './styles'; export const Directions = { @@ -16,70 +19,94 @@ type Direction = (typeof Directions)[keyof typeof Directions]; export class NetworkSelector extends LitElement { static styles = styles; - @property({ - type: Boolean - }) - disabled = false; + @state() + _isDropdownOpen = false; - @property({ - type: Boolean - }) - icons = true; + @property({ type: Boolean }) + disabled = false; - @property({ - type: String - }) + @property({ type: String }) direction?: Direction; - @property({ - type: Object - }) - selected?: Domain; + @property({ type: Object }) + selectedNetwork?: Domain; - @property({ - attribute: false - }) - onNetworkSelected?: (network?: Domain) => void; + @property({ attribute: false }) + onNetworkSelected: (network?: Domain) => void = () => {}; - @property({ - type: Array - }) + @property({ type: Array }) networks: Domain[] = []; - onChange({ target }: Event): void { - const { value } = target as HTMLOptionElement; - const network = this.networks.find((n) => String(n.chainId) == value); - this.onNetworkSelected?.(network); + _toggleDropdown = (): void => { + this._isDropdownOpen = !this._isDropdownOpen; + }; + + _selectOption(option: Domain, event: Event): void { + event.stopPropagation(); + this.selectedNetwork = option; + this._isDropdownOpen = false; + this.onNetworkSelected?.(option); } - renderEntries(): Generator | HTMLTemplateResult { - if (this.networks) { - return map(this.networks, (entry: Domain) => { - // TODO: render network icon - return html``; - }); - } - return html``; + _renderNetworkIcon(name: string): HTMLTemplateResult { + return networkIconsMap[name] || networkIconsMap.default; + } + + _renderEntries(): Generator { + return map( + this.networks, + (network: Domain) => html` + + ` + ); + } + + _renderTriggerContent(): HTMLTemplateResult { + return when( + this.selectedNetwork, + () => + html`${this._renderNetworkIcon(this.selectedNetwork!.name)} + + ${capitalize(this.selectedNetwork!.name)} + `, + () => html`Select Network` + ); } render(): HTMLTemplateResult { - return html`
+ return html`
-
- -
+ ${this._renderEntries()} +
+
`; } } + declare global { interface HTMLElementTagNameMap { 'sygma-network-selector': NetworkSelector; diff --git a/packages/widget/src/components/network-selector/styles.ts b/packages/widget/src/components/network-selector/styles.ts index 845dd49d..1b36041d 100644 --- a/packages/widget/src/components/network-selector/styles.ts +++ b/packages/widget/src/components/network-selector/styles.ts @@ -2,22 +2,24 @@ import { css } from 'lit'; export const styles = css` .selectorContainer { - border-radius: 24px; - border: 1px solid #e4e4e7; + border-radius: 1.5rem; + border: 0.0625rem solid #e4e4e7; display: flex; - width: 314px; - padding: 12px 16px; + width: 100%; + max-width: 19.625rem; + max-height: 4.625rem; + padding: 0.75rem 1rem; flex-direction: column; justify-content: center; align-items: stretch; - gap: 4px; + gap: 0.25rem; } .directionLabel { color: #a1a1aa; - font-size: 14px; + font-size: 0.875rem; font-weight: 500; - line-height: 20px; + line-height: 1.25rem; display: flex; flex-direction: column; justify-content: center; @@ -30,15 +32,89 @@ export const styles = css` .selector { width: 100%; color: #525252; - font-size: 18px; + font-size: 1.125rem; font-weight: 500; - line-height: 26px; + line-height: 1.625rem; border: none; } .selectorSection { display: flex; align-items: center; - gap: 12px; + gap: 0.75rem; + } + + .dropdown { + position: relative; + width: 100%; + } + + .selectedNetwork { + display: flex; + align-items: center; + } + + .dropdownTrigger { + width: 100%; + display: flex; + justify-content: space-between; + align-items: center; + cursor: pointer; + padding: 0.25rem 0; + box-sizing: border-box; + } + + .chevron { + transform: rotate(0deg); + transition: transform 0.3s ease; + + &.open { + transform: rotate(180deg); + } + } + + .dropdownContent { + display: none; + position: absolute; + background-color: white; + width: 100%; + border-radius: 0.75rem; + border: 0.0625rem solid #f3f4f6; + box-shadow: + 0 0.25rem 0.375rem -0.0625rem rgba(0, 0, 0, 0.1), + 0 0.125rem 0.25rem -0.0625rem rgba(0, 0, 0, 0.06); + z-index: 1; + margin-top: 1rem; + } + + .show { + display: block; + } + + .dropdownOption { + display: flex; + align-items: center; + padding: 0.75rem 1rem; + cursor: pointer; + transition: background-color 0.3s ease; + + svg { + max-width: 1.43656rem; + width: 100%; + } + + &:hover { + background-color: #e9e4dd; + } + } + + .networkIcon { + display: block; + width: 1.25rem; + height: 1.25rem; + } + + .networkName { + margin-left: 0.5rem; } `; diff --git a/packages/widget/src/styles.ts b/packages/widget/src/styles.ts index 3b8120f7..5b009932 100644 --- a/packages/widget/src/styles.ts +++ b/packages/widget/src/styles.ts @@ -28,6 +28,10 @@ export const styles = css` font-family: Inter, sans-serif; } + .networkSelectionWrapper { + margin: 1rem 0 0.5rem 0; + } + .connectAccount { display: flex; justify-content: flex-end; diff --git a/packages/widget/src/widget.ts b/packages/widget/src/widget.ts index c2e5cd1c..7c481c84 100644 --- a/packages/widget/src/widget.ts +++ b/packages/widget/src/widget.ts @@ -22,7 +22,7 @@ class SygmaProtocolWidget extends LitElement {
${switchNetworkIcon} Connect Wallet
-
+
{ - const el = await fixture(html` + + it('renders text when no network selected', async () => { + const el = await fixture(html` `); - assert.equal(el.shadowRoot!.querySelectorAll('.network-option').length, 1); + + const networkOption = el.shadowRoot!.querySelector( + '.selectedNetwork' + ) as HTMLDivElement; + + assert.ok( + networkOption, + 'Network option should be present in the component' + ); + assert.equal( - getDiffableHTML(el.shadowRoot!.querySelector('.selector')!), - getDiffableHTML( - `` - ) + getDiffableHTML(networkOption.textContent?.trim() ?? ''), + getDiffableHTML('Select Network') ); }); + + it('toggles dropdown on click', async () => { + const networks = [ + { id: '1', name: 'ethereum' }, + { id: '2', name: 'khala' } + ]; + + const el = await fixture( + html`` + ); + const dropdownTrigger = el.shadowRoot!.querySelector( + '.dropdownTrigger' + ) as HTMLDivElement; + + assert.ok( + dropdownTrigger, + 'Dropdown trigger should be present in the component' + ); + + // Initial state should be closed + assert.isFalse(el._isDropdownOpen); + + // Simulate click to open dropdown + dropdownTrigger?.click(); + await el.updateComplete; + assert.isTrue(el._isDropdownOpen); + + // Simulate another click to close dropdown + dropdownTrigger?.click(); + await el.updateComplete; + assert.isFalse(el._isDropdownOpen); + }); + it('triggers callback on network selected', async () => { const mockNetworkSelectHandler = vi.fn(); const network = { id: 0, chainId: 1, name: 'Test', type: 'evm' }; - const el = await fixture(html` + const el = await fixture(html` `); - const networkOptions = el.shadowRoot!.querySelectorAll('.network-option'); - assert.equal(networkOptions.length, 2); - const listener = oneEvent( - el.shadowRoot!.querySelector('.selector')!, - 'change', - false - ); - (el.shadowRoot!.querySelector('.selector') as HTMLSelectElement).value = - '1'; - el.shadowRoot!.querySelector('.selector')!.dispatchEvent( - new Event('change') - ); - (networkOptions[1] as HTMLOptionElement).selected = true; - await listener; + + // Simulate selecting a network + const firstOption = el.shadowRoot!.querySelector( + '.dropdownOption' + ) as HTMLDivElement; + assert.ok(firstOption, 'First option should be present in the component'); + firstOption.click(); + await el.updateComplete; + assert.equal(mockNetworkSelectHandler.mock.calls.length, 1); assert.deepEqual(mockNetworkSelectHandler.mock.lastCall, [network]); }); From 9fa87ad24762518cdcff10a0dd1aad5c844ce1dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Riquelme=20Guzm=C3=A1n?= Date: Thu, 1 Feb 2024 09:07:31 -0300 Subject: [PATCH 2/2] feat: address to transfer (#56) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description This PR adds toggle and input to introduce address to transfer ## Related Issue Or Context Closes: #20 ## How Has This Been Tested? Testing details. ## Types of changes - [ ] Bug fix (non-breaking change which fixes an issue) - [x] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Documentation ## Checklist: - [ ] I have commented my code, particularly in hard-to-understand areas. - [ ] I have ensured that all acceptance criteria (or expected behavior) from issue are met - [ ] I have updated the documentation locally and in sygma-docs. - [ ] I have added tests to cover my changes. - [ ] I have ensured that all the checks are passing and green, I've signed the CLA bot --------- Signed-off-by: Marin Petrunic Co-authored-by: Marin Petrunic Co-authored-by: Filip Štoković Co-authored-by: Marin Petrunić Co-authored-by: Filip Štoković <59089574+sztok7@users.noreply.github.com> --- packages/widget/package.json | 6 +- .../components/address-input/address-input.ts | 80 +++++ .../src/components/address-input/index.ts | 1 + .../src/components/address-input/styles.ts | 51 +++ .../src/components/amount-selector/styles.ts | 6 +- packages/widget/src/components/index.ts | 1 + packages/widget/src/controllers/widget.ts | 6 + packages/widget/src/styles.ts | 28 +- packages/widget/src/utils/index.ts | 32 ++ packages/widget/src/widget.ts | 9 + .../address-input/address-input.test.ts | 271 ++++++++++++++++ packages/widget/vite.config.ts | 2 +- yarn.lock | 298 ++---------------- 13 files changed, 507 insertions(+), 284 deletions(-) create mode 100644 packages/widget/src/components/address-input/address-input.ts create mode 100644 packages/widget/src/components/address-input/index.ts create mode 100644 packages/widget/src/components/address-input/styles.ts create mode 100644 packages/widget/tests/unit/components/address-input/address-input.test.ts diff --git a/packages/widget/package.json b/packages/widget/package.json index c1cfd32c..9140ed42 100644 --- a/packages/widget/package.json +++ b/packages/widget/package.json @@ -27,7 +27,7 @@ }, "author": "Sygmaprotocol Product Team", "dependencies": { - "@buildwithsygma/sygma-sdk-core": "^2.6.0", + "@buildwithsygma/sygma-sdk-core": "^2.6.2", "@ethersproject/abstract-signer": "^5.7.0", "@ethersproject/contracts": "^5.7.0", "@ethersproject/providers": "^5.7.2", @@ -36,6 +36,8 @@ "@lit/reactive-element": "^2.0.3", "@polkadot/api": "^10.11.2", "@polkadot/extension-dapp": "^0.46.6", + "@polkadot/keyring": "^12.6.2", + "@polkadot/util": "^12.6.2", "ethers": "5.7.2", "events": "^3.3.0", "lit": "3.0.0" @@ -45,7 +47,7 @@ "@open-wc/testing-helpers": "^3.0.0", "eslint": "^8.48.0", "eslint-plugin-lit": "^1.9.1", - "jsdom": "^23.2.0", + "happy-dom": "^13.3.1", "lit-analyzer": "^2.0.3", "rollup-plugin-visualizer": "^5.9.2", "typescript": "5.2.2", diff --git a/packages/widget/src/components/address-input/address-input.ts b/packages/widget/src/components/address-input/address-input.ts new file mode 100644 index 00000000..cc2a01a9 --- /dev/null +++ b/packages/widget/src/components/address-input/address-input.ts @@ -0,0 +1,80 @@ +import { LitElement, html } from 'lit'; +import type { HTMLTemplateResult } from 'lit'; +import { customElement, property, state } from 'lit/decorators.js'; +import { ifDefined } from 'lit/directives/if-defined.js'; +import { Network } from '@buildwithsygma/sygma-sdk-core'; +import { when } from 'lit/directives/when.js'; +import { validateAddress } from '../../utils'; +import { styles } from './styles'; + +@customElement('sygma-address-input') +export class AddressInput extends LitElement { + static styles = styles; + @property({ + type: String + }) + address: string = ''; + + @property({ attribute: false }) + onAddressChange: (address: string) => void = () => {}; + + @property({ + type: String + }) + network: Network = Network.EVM; + + @state() + errorMessage: string | null = null; + + connectedCallback(): void { + super.connectedCallback(); + this.handleAddressChange(this.address); + } + + private handleAddressChange = (value: string): void => { + const trimedValue = value.trim(); + + if (this.errorMessage) { + this.errorMessage = null; + } + + if (!trimedValue) { + return; + } + + this.errorMessage = validateAddress(trimedValue, this.network); + + if (!this.errorMessage) { + void this.onAddressChange(trimedValue); + } + }; + + render(): HTMLTemplateResult { + return html`
+
+ + +
+
`; + } +} + +declare global { + interface HTMLElementTagNameMap { + 'sygma-address-input': AddressInput; + } +} diff --git a/packages/widget/src/components/address-input/index.ts b/packages/widget/src/components/address-input/index.ts new file mode 100644 index 00000000..3f29279d --- /dev/null +++ b/packages/widget/src/components/address-input/index.ts @@ -0,0 +1 @@ +export { AddressInput } from './address-input'; diff --git a/packages/widget/src/components/address-input/styles.ts b/packages/widget/src/components/address-input/styles.ts new file mode 100644 index 00000000..f5778c64 --- /dev/null +++ b/packages/widget/src/components/address-input/styles.ts @@ -0,0 +1,51 @@ +import { css } from 'lit'; + +export const styles = css` + .inputAddressSection { + display: flex; + flex-direction: column; + justify-content: center; + gap: 0.5rem; + min-height: 7.75rem; // TOO: remove this hardcoded value + } + + .inputAddressContainer { + display: flex; + flex-direction: column; + width: 100%; + min-height: 7.75rem; // TOO: remove this hardcoded value + gap: 0.5rem; + } + + .inputAddress { + border-radius: 1.5rem; + border: 0.063rem solid var(--zinc-200); + font-size: 0.875rem; + text-align: center; + resize: none; + box-sizing: border-box; + overflow: hidden; + padding: 1rem; + } + + .inputAddress:focus { + outline: none; + border: 0.063rem solid var(--zinc-200); + } + + .error { + border-color: red; + } + + .errorMessage { + color: red; + font-weight: 300; + font-size: 0.75rem; + } + + .labelContainer { + display: flex; + flex-direction: row; + justify-content: space-between; + } +`; diff --git a/packages/widget/src/components/amount-selector/styles.ts b/packages/widget/src/components/amount-selector/styles.ts index c893ab35..f60e2615 100644 --- a/packages/widget/src/components/amount-selector/styles.ts +++ b/packages/widget/src/components/amount-selector/styles.ts @@ -15,7 +15,7 @@ export const styles = css` display: flex; width: 100%; justify-content: flex-start; - color: #525252; + color: var(--neutral-600); font-size: 14px; font-weight: 500; line-height: 20px; /* 142.857% */ @@ -30,7 +30,7 @@ export const styles = css` .amountSelectorInput { border: none; - color: #525252; + color: var(--neutral-600); font-size: 34px; font-weight: 500; line-height: 40px; @@ -52,7 +52,7 @@ export const styles = css` } .maxButton { - color: #2563eb; + color: var(--blue-600); border: none; background: none; font-weight: 500; diff --git a/packages/widget/src/components/index.ts b/packages/widget/src/components/index.ts index 35b47082..67dd98b6 100644 --- a/packages/widget/src/components/index.ts +++ b/packages/widget/src/components/index.ts @@ -1,2 +1,3 @@ export { AmountSelector } from './amount-selector'; export { NetworkSelector } from './network-selector'; +export { AddressInput } from './address-input'; diff --git a/packages/widget/src/controllers/widget.ts b/packages/widget/src/controllers/widget.ts index ca9a119b..d13f82ff 100644 --- a/packages/widget/src/controllers/widget.ts +++ b/packages/widget/src/controllers/widget.ts @@ -18,6 +18,7 @@ export class WidgetController implements ReactiveController { public supportedSourceNetworks: Domain[] = []; public supportedDestinationNetworks: Domain[] = []; public supportedResources: Resource[] = []; + public destinatonAddress?: string = ''; //@ts-expect-error it will be used private assetTransfer?: EVMAssetTransfer | SubstrateAssetTransfer; @@ -126,4 +127,9 @@ export class WidgetController implements ReactiveController { console.log('resource amount', amount); this.resourceAmount = amount; }; + + onDestinationAddressChange = (address: string): void => { + console.log('destination address', address); + this.destinatonAddress = address; + }; } diff --git a/packages/widget/src/styles.ts b/packages/widget/src/styles.ts index 5b009932..9ee30549 100644 --- a/packages/widget/src/styles.ts +++ b/packages/widget/src/styles.ts @@ -1,6 +1,17 @@ import { css } from 'lit'; export const styles = css` + :host { + --zinc-200: #e4e4e7; + --zinc-400: #a1a1aa; + --white: #fff; + --gray-100: #f3f4f6; + --neutral-600: #525252; + --primary-300: #a5b4fc; + --primary-500: #6366f1; + --blue-600: #2563eb; + } + @font-face { font-family: 'Inter'; font-style: normal; @@ -15,11 +26,10 @@ export const styles = css` gap: 16px; padding: 24px; - width: 350px; /* TODO: remove these hardcoded values */ - height: 476px; /* TODO: ↑ */ + width: 21.875rem; /* TODO: remove these hardcoded values */ border-radius: 12px; - border: 1px solid #f3f4f6; - background-color: #fff; + border: 1px solid var(--gray-100); + background-color: var(--white); box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), @@ -47,7 +57,7 @@ export const styles = css` border-radius: 16px; border: none; - background-color: #a5b4fc; + background-color: var(--primary-300); color: #ffffff; width: 314px; /* TODO: remove these hardcoded values */ @@ -62,7 +72,7 @@ export const styles = css` } .actionButton:active { - background-color: #6366f1; + background-color: var(--primary-500); } .actionButtonReady { @@ -75,13 +85,13 @@ export const styles = css` padding: 12px 20px; border-radius: 16px; - background-color: #6366f1; + background-color: var(--primary-500); color: #ffffff; border: none; } .actionButtonReady:active { - background-color: #a5b4fc; + background-color: var(--primary-300); } .actionButtonReady:hover { @@ -94,7 +104,7 @@ export const styles = css` gap: 6px; align-self: flex-start; - color: #525252; + color: var(--neutral-600); font-size: 12px; line-height: 150%; } diff --git a/packages/widget/src/utils/index.ts b/packages/widget/src/utils/index.ts index 84226984..887615e6 100644 --- a/packages/widget/src/utils/index.ts +++ b/packages/widget/src/utils/index.ts @@ -1,5 +1,9 @@ import type { HTMLTemplateResult } from 'lit'; import { html } from 'lit'; +import { decodeAddress, encodeAddress } from '@polkadot/keyring'; +import { hexToU8a, isHex } from '@polkadot/util'; +import { Network } from '@buildwithsygma/sygma-sdk-core'; +import { ethers } from 'ethers'; import { baseNetworkIcon, cronosNetworkIcon, @@ -46,3 +50,31 @@ export const capitalize = (s: string): string => { const rest = s.slice(1); return `${firstLetter}${rest}`; }; + +export const validateSubstrateAddress = (address: string): boolean => { + try { + encodeAddress(isHex(address) ? hexToU8a(address) : decodeAddress(address)); + return true; + } catch (error) { + return false; + } +}; + +export const validateAddress = ( + address: string, + network: Network +): string | null => { + switch (network) { + case Network.SUBSTRATE: { + const validPolkadotAddress = validateSubstrateAddress(address); + return validPolkadotAddress ? null : 'invalid Substrate address'; + } + case Network.EVM: { + const isAddress = ethers.utils.isAddress(address); + + return isAddress ? null : 'invalid Ethereum address'; + } + default: + return 'unsupported network'; + } +}; diff --git a/packages/widget/src/widget.ts b/packages/widget/src/widget.ts index 7c481c84..be57cc16 100644 --- a/packages/widget/src/widget.ts +++ b/packages/widget/src/widget.ts @@ -6,6 +6,7 @@ import { switchNetworkIcon, sygmaLogo } from './assets'; import { WidgetController } from './controllers/widget'; import './components/network-selector'; import './components/amount-selector'; +import './components/address-input'; import { Directions } from './components/network-selector/network-selector'; @customElement('sygmaprotocol-widget') @@ -51,6 +52,14 @@ class SygmaProtocolWidget extends LitElement { >
+
+ + +