From a7c7ce04831f2ecde7ad411ca5967cec52b402e5 Mon Sep 17 00:00:00 2001 From: Cali93 <32299095+Cali93@users.noreply.github.com> Date: Tue, 21 Nov 2023 18:52:30 +0200 Subject: [PATCH 01/96] feat(swap): add swap view --- packages/core/index.ts | 3 + .../core/src/controllers/RouterController.ts | 1 + .../core/src/controllers/SwapApiController.ts | 111 ++++++++++++++++ packages/core/src/utils/CoreHelperUtil.ts | 19 +++ packages/core/src/utils/TypeUtil.ts | 3 + packages/scaffold/index.ts | 1 + .../scaffold/src/modal/w3m-router/index.ts | 2 + .../scaffold/src/partials/w3m-header/index.ts | 3 +- .../scaffold/src/views/w3m-swap-view/index.ts | 122 ++++++++++++++++++ .../src/views/w3m-swap-view/styles.ts | 14 ++ .../assets/svg/swapHorizontalRoundedBold.ts | 5 + packages/ui/src/components/wui-icon/index.ts | 2 + packages/ui/src/utils/TypeUtil.ts | 1 + 13 files changed, 286 insertions(+), 1 deletion(-) create mode 100644 packages/core/src/controllers/SwapApiController.ts create mode 100644 packages/scaffold/src/views/w3m-swap-view/index.ts create mode 100644 packages/scaffold/src/views/w3m-swap-view/styles.ts create mode 100644 packages/ui/src/assets/svg/swapHorizontalRoundedBold.ts diff --git a/packages/core/index.ts b/packages/core/index.ts index 2e701ec5f5..a12584f159 100644 --- a/packages/core/index.ts +++ b/packages/core/index.ts @@ -55,6 +55,9 @@ export type { EventsControllerState } from './src/controllers/EventsController.j export { TransactionsController } from './src/controllers/TransactionsController.js' export type { TransactionsControllerState } from './src/controllers/TransactionsController.js' +export { SwapApiController } from './src/controllers/SwapApiController.js' +export type { SwapApiControllerState } from './src/controllers/SwapApiController.js' + // -- Utils ------------------------------------------------------------------- export { AssetUtil } from './src/utils/AssetUtil.js' export { ConstantsUtil } from './src/utils/ConstantsUtil.js' diff --git a/packages/core/src/controllers/RouterController.ts b/packages/core/src/controllers/RouterController.ts index bdd7469ac8..be524db12b 100644 --- a/packages/core/src/controllers/RouterController.ts +++ b/packages/core/src/controllers/RouterController.ts @@ -36,6 +36,7 @@ export interface RouterControllerState { | 'WhatIsANetwork' | 'WhatIsAWallet' | 'WhatIsABuy' + | 'Swap' history: RouterControllerState['view'][] data?: { connector?: Connector diff --git a/packages/core/src/controllers/SwapApiController.ts b/packages/core/src/controllers/SwapApiController.ts new file mode 100644 index 0000000000..4b953156ff --- /dev/null +++ b/packages/core/src/controllers/SwapApiController.ts @@ -0,0 +1,111 @@ +import { subscribeKey as subKey } from 'valtio/utils' +import { proxy } from 'valtio/vanilla' +import { CoreHelperUtil } from '../utils/CoreHelperUtil.js' +import { NetworkController } from './NetworkController.js' +import { FetchUtil } from '../utils/FetchUtil.js' +import { AccountController } from './AccountController.js' + +// -- Types --------------------------------------------- // +export interface SwapApiControllerState { + sourceTokenAddress?: `0x${string}` + toTokenAddress?: `0x${string}` + sourceTokenAmount?: string + slippage?: number + disableEstimate?: boolean + allowPartialFill?: boolean + loading?: boolean +} + +type StateKey = keyof SwapApiControllerState + +// -- State --------------------------------------------- // +const state = proxy({ + sourceTokenAddress: undefined, + toTokenAddress: undefined, + sourceTokenAmount: undefined, + slippage: 1, + disableEstimate: false, + allowPartialFill: false, + loading: false +}) + +// -- Controller ---------------------------------------- // +export const SwapApiController = { + state, + + subscribeKey(key: K, callback: (value: SwapApiControllerState[K]) => void) { + return subKey(state, key, callback) + }, + + _getApi() { + const chainId = CoreHelperUtil.getEvmChainId(NetworkController.state.caipNetwork?.id) + const baseUrl = CoreHelperUtil.get1inchApiUrl(chainId) + + return new FetchUtil({ baseUrl }) + }, + + _getSwapParams() { + const { address } = AccountController.state + if (!address) { + throw new Error('No address found to swap the tokens from.') + } + + return { + fromAddress: address, + sourceTokenAddress: state.sourceTokenAddress, + toTokenAddress: state.toTokenAddress, + sourceTokenAmount: state.sourceTokenAmount, + slippage: state.slippage + } + }, + + setSourceTokenAddress(sourceTokenAddress: `0x${string}`) { + state.sourceTokenAddress = sourceTokenAddress + }, + + setSourceTokenAmount(swapFromAmount: string) { + state.sourceTokenAmount = swapFromAmount + }, + + setToTokenAddress(toTokenAddress: `0x${string}`) { + state.toTokenAddress = toTokenAddress + }, + + setSlippage(slippage: number) { + state.slippage = slippage + }, + + setLoading(isLoading: boolean) { + state.loading = isLoading + }, + + async swap(isFusion?: boolean) { + const api = this._getApi() + const path = `${api.baseUrl}/${isFusion ? 'fusion' : 'swap'}` + const body = this._getSwapParams() + + const swapTransactionRes = await api.post({ + path, + body + }) + + return swapTransactionRes + }, + + async hasTokenAllowance() { + const api = this._getApi() + const path = `${api.baseUrl}/approve/allowance` + const { sourceTokenAddress, fromAddress, sourceTokenAmount } = this._getSwapParams() + + const res = await api.post<{ allowance: string }>({ + path, + body: { sourceTokenAddress, fromAddress } + }) + + if (res?.allowance && sourceTokenAmount) { + return BigInt(res.allowance) >= BigInt(sourceTokenAmount) + } + + return false + } +} diff --git a/packages/core/src/utils/CoreHelperUtil.ts b/packages/core/src/utils/CoreHelperUtil.ts index d260d5a227..7db22dcccd 100644 --- a/packages/core/src/utils/CoreHelperUtil.ts +++ b/packages/core/src/utils/CoreHelperUtil.ts @@ -50,6 +50,16 @@ export const CoreHelperUtil = { return caipAddress.split(':')[2] }, + getEvmChainId(caipNetworkId?: `${string}:${string}`) { + const strChainId = caipNetworkId?.split(':')?.[1] + if (!strChainId) { + // Default to Ethereum mainnet + return 1 + } + + return parseInt(strChainId, 10) + }, + async wait(milliseconds: number) { return new Promise(resolve => { setTimeout(resolve, milliseconds) @@ -215,6 +225,7 @@ export const CoreHelperUtil = { return 'Unknown error' }, + sortRequestedNetworks( approvedIds: `${string}:${string}`[] | undefined, requestedNetworks: CaipNetwork[] = [] @@ -244,6 +255,7 @@ export const CoreHelperUtil = { return requestedNetworks }, + calculateBalance(array: Balance[]) { let sum = 0 for (const item of array) { @@ -252,10 +264,17 @@ export const CoreHelperUtil = { return sum }, + formatTokenBalance(number: number) { const roundedNumber = number.toFixed(2) const [dollars, pennies] = roundedNumber.split('.') return { dollars, pennies } + }, + + get1inchApiUrl(chainId: number) { + return CoreHelperUtil.isRestrictedRegion() + ? `https://swap.walletconnect.org/${chainId}` + : `https://swap.walletconnect.com/${chainId}` } } diff --git a/packages/core/src/utils/TypeUtil.ts b/packages/core/src/utils/TypeUtil.ts index 492714caed..e8db779b6d 100644 --- a/packages/core/src/utils/TypeUtil.ts +++ b/packages/core/src/utils/TypeUtil.ts @@ -325,6 +325,9 @@ export type Event = network: string } } + | { + event: 'CLICK_SWAP' + } // Onramp Types export type DestinationWallet = { diff --git a/packages/scaffold/index.ts b/packages/scaffold/index.ts index f8df152827..6b3d99e1fc 100644 --- a/packages/scaffold/index.ts +++ b/packages/scaffold/index.ts @@ -21,6 +21,7 @@ export * from './src/views/w3m-onramp-activity-view/index.js' export * from './src/views/w3m-onramp-fiat-select-view/index.js' export * from './src/views/w3m-onramp-providers-view/index.js' export * from './src/views/w3m-onramp-tokens-select-view/index.js' +export * from './src/views/w3m-swap-view/index.js' export * from './src/views/w3m-transactions-view/index.js' export * from './src/views/w3m-what-is-a-network-view/index.js' export * from './src/views/w3m-what-is-a-wallet-view/index.js' diff --git a/packages/scaffold/src/modal/w3m-router/index.ts b/packages/scaffold/src/modal/w3m-router/index.ts index b6643c7808..f80d5cf191 100644 --- a/packages/scaffold/src/modal/w3m-router/index.ts +++ b/packages/scaffold/src/modal/w3m-router/index.ts @@ -117,6 +117,8 @@ export class W3mRouter extends LitElement { return html`` case 'WalletCompatibleNetworks': return html`` + case 'Swap': + return html`` default: return html`` } diff --git a/packages/scaffold/src/partials/w3m-header/index.ts b/packages/scaffold/src/partials/w3m-header/index.ts index 57dbf54e8e..f3eca00224 100644 --- a/packages/scaffold/src/partials/w3m-header/index.ts +++ b/packages/scaffold/src/partials/w3m-header/index.ts @@ -52,7 +52,8 @@ function headings() { OnRampTokenSelect: 'Select Token', OnRampFiatSelect: 'Select Currency', WalletReceive: 'Receive', - WalletCompatibleNetworks: 'Compatible Networks' + WalletCompatibleNetworks: 'Compatible Networks', + Swap: 'Swap' } } diff --git a/packages/scaffold/src/views/w3m-swap-view/index.ts b/packages/scaffold/src/views/w3m-swap-view/index.ts new file mode 100644 index 0000000000..492ee1e753 --- /dev/null +++ b/packages/scaffold/src/views/w3m-swap-view/index.ts @@ -0,0 +1,122 @@ +import { customElement } from '@web3modal/ui' +import { LitElement, html } from 'lit' +import { state } from 'lit/decorators.js' +import styles from './styles.js' +import { SwapApiController } from '@web3modal/core' + +@customElement('w3m-swap-view') +export class W3mSwapView extends LitElement { + public static override styles = styles + + // -- State & Properties -------------------------------- // + @state() private loading = SwapApiController.state.loading + + // -- Lifecycle ----------------------------------------- // + public constructor() { + super() + } + + private getInputElement(el: HTMLElement) { + if (el.shadowRoot?.querySelector('input')) { + return el.shadowRoot.querySelector('input') + } + + return null + } + + private handleInput(e: InputEvent) { + const inputElement = e.target as HTMLElement + const input = this.getInputElement(inputElement) + + if (input) { + const inputValue = input.value + SwapApiController.setSourceTokenAmount(inputValue) + } + } + + private onSwap() { + const amount = SwapApiController.state.sourceTokenAmount + // eslint-disable-next-line no-console + console.log({ amount }) + } + + // -- Render -------------------------------------------- // + public override render() { + return html` + + ${this.loading ? this.templateLoading() : this.templateSwap()} + + ` + } + + // -- Private ------------------------------------------- // + private templateSwap() { + return html` + + + Swap tokens + + +
${this.templateSwapTokenSelector()}
+
${this.templateSwapTokenSelector()}
+
+ Swap +
+ ` + } + + private templateLoading() { + return html` + + Swaping + + ` + } + + private templateSwapTokenSelector() { + return html` + Amount + this.handleInput(e)} + > + Token + + ` + } +} + +declare global { + interface HTMLElementTagNameMap { + 'w3m-swap-view': W3mSwapView + } +} diff --git a/packages/scaffold/src/views/w3m-swap-view/styles.ts b/packages/scaffold/src/views/w3m-swap-view/styles.ts new file mode 100644 index 0000000000..125ae89bd0 --- /dev/null +++ b/packages/scaffold/src/views/w3m-swap-view/styles.ts @@ -0,0 +1,14 @@ +import { css } from 'lit' + +export default css` + :host > wui-flex:first-child { + height: 500px; + overflow-y: auto; + overflow-x: hidden; + scrollbar-width: none; + } + + wui-loading-hexagon { + position: absolute; + } +` diff --git a/packages/ui/src/assets/svg/swapHorizontalRoundedBold.ts b/packages/ui/src/assets/svg/swapHorizontalRoundedBold.ts new file mode 100644 index 0000000000..302e1ade80 --- /dev/null +++ b/packages/ui/src/assets/svg/swapHorizontalRoundedBold.ts @@ -0,0 +1,5 @@ +import { svg } from 'lit' + +export const swapHorizontalRoundedBoldSvg = svg` + +` diff --git a/packages/ui/src/components/wui-icon/index.ts b/packages/ui/src/components/wui-icon/index.ts index f168dc62db..93678a1eb3 100644 --- a/packages/ui/src/components/wui-icon/index.ts +++ b/packages/ui/src/components/wui-icon/index.ts @@ -54,6 +54,7 @@ import { sendSvg } from '../../assets/svg/send.js' import { swapHorizontalSvg } from '../../assets/svg/swapHorizontal.js' import { swapHorizontalBoldSvg } from '../../assets/svg/swapHorizontalBold.js' import { swapHorizontalMediumSvg } from '../../assets/svg/swapHorizontalMedium.js' +import { swapHorizontalRoundedBoldSvg } from '../../assets/svg/swapHorizontalRoundedBold.js' import { swapVerticalSvg } from '../../assets/svg/swapVertical.js' import { telegramSvg } from '../../assets/svg/telegram.js' import { twitchSvg } from '../../assets/svg/twitch.js' @@ -124,6 +125,7 @@ const svgOptions: Record> = { swapHorizontal: swapHorizontalSvg, swapHorizontalMedium: swapHorizontalMediumSvg, swapHorizontalBold: swapHorizontalBoldSvg, + swapHorizontalRoundedBold: swapHorizontalRoundedBoldSvg, swapVertical: swapVerticalSvg, telegram: telegramSvg, twitch: twitchSvg, diff --git a/packages/ui/src/utils/TypeUtil.ts b/packages/ui/src/utils/TypeUtil.ts index ac9c03fe60..83989646bc 100644 --- a/packages/ui/src/utils/TypeUtil.ts +++ b/packages/ui/src/utils/TypeUtil.ts @@ -144,6 +144,7 @@ export type IconType = | 'swapHorizontal' | 'swapHorizontalBold' | 'swapHorizontalMedium' + | 'swapHorizontalRoundedBold' | 'swapVertical' | 'telegram' | 'twitch' From 9693a2a293e84abab8ae764c04edd797a1ae5e4e Mon Sep 17 00:00:00 2001 From: Enes Date: Wed, 22 Nov 2023 14:12:04 +0300 Subject: [PATCH 02/96] refactor: update swap view ui (#1492) --- .../composites/wui-token-list-item.stories.ts | 34 +++ .../core/src/controllers/RouterController.ts | 1 + packages/core/src/utils/TypeUtil.ts | 4 + packages/scaffold/index.ts | 1 + .../scaffold/src/modal/w3m-router/index.ts | 2 + .../scaffold/src/partials/w3m-header/index.ts | 3 +- .../views/w3m-swap-select-token-view/index.ts | 227 ++++++++++++++++++ .../w3m-swap-select-token-view/styles.ts | 37 +++ .../scaffold/src/views/w3m-swap-view/index.ts | 105 ++++++-- .../src/views/w3m-swap-view/styles.ts | 111 ++++++++- packages/ui/index.ts | 1 + .../ui/src/composites/wui-button/styles.ts | 1 + .../composites/wui-token-list-item/index.ts | 61 +++++ .../composites/wui-token-list-item/styles.ts | 32 +++ 14 files changed, 593 insertions(+), 27 deletions(-) create mode 100644 apps/gallery/stories/composites/wui-token-list-item.stories.ts create mode 100644 packages/scaffold/src/views/w3m-swap-select-token-view/index.ts create mode 100644 packages/scaffold/src/views/w3m-swap-select-token-view/styles.ts create mode 100644 packages/ui/src/composites/wui-token-list-item/index.ts create mode 100644 packages/ui/src/composites/wui-token-list-item/styles.ts diff --git a/apps/gallery/stories/composites/wui-token-list-item.stories.ts b/apps/gallery/stories/composites/wui-token-list-item.stories.ts new file mode 100644 index 0000000000..9a7d8698c8 --- /dev/null +++ b/apps/gallery/stories/composites/wui-token-list-item.stories.ts @@ -0,0 +1,34 @@ +import type { Meta } from '@storybook/web-components' +import '@web3modal/ui/src/composites/wui-list-item' +import type { WuiTokenListItem } from '@web3modal/ui/src/composites/wui-token-list-item' +import { html } from 'lit' +import '../../components/gallery-container' +import { networkImageSrc } from '../../utils/PresetUtils' + +type Component = Meta + +export default { + title: 'Composites/wui-token-list-item', + args: { + name: 'Ethereum', + symbol: 'ETH', + price: '$1,740.72', + amount: '0.867', + imageSrc: networkImageSrc + }, + argTypes: {} +} as Component + +export const Default: Component = { + render: args => + html` + + + ` +} diff --git a/packages/core/src/controllers/RouterController.ts b/packages/core/src/controllers/RouterController.ts index be524db12b..6d77df74cb 100644 --- a/packages/core/src/controllers/RouterController.ts +++ b/packages/core/src/controllers/RouterController.ts @@ -37,6 +37,7 @@ export interface RouterControllerState { | 'WhatIsAWallet' | 'WhatIsABuy' | 'Swap' + | 'SwapSelectToken' history: RouterControllerState['view'][] data?: { connector?: Connector diff --git a/packages/core/src/utils/TypeUtil.ts b/packages/core/src/utils/TypeUtil.ts index e8db779b6d..a9fdabeac7 100644 --- a/packages/core/src/utils/TypeUtil.ts +++ b/packages/core/src/utils/TypeUtil.ts @@ -328,6 +328,10 @@ export type Event = | { event: 'CLICK_SWAP' } + | { + type: 'track' + event: 'CLICK_SELECT_TOKEN_TO_SWAP' + } // Onramp Types export type DestinationWallet = { diff --git a/packages/scaffold/index.ts b/packages/scaffold/index.ts index 6b3d99e1fc..6a54f1f4d2 100644 --- a/packages/scaffold/index.ts +++ b/packages/scaffold/index.ts @@ -22,6 +22,7 @@ export * from './src/views/w3m-onramp-fiat-select-view/index.js' export * from './src/views/w3m-onramp-providers-view/index.js' export * from './src/views/w3m-onramp-tokens-select-view/index.js' export * from './src/views/w3m-swap-view/index.js' +export * from './src/views/w3m-swap-select-token-view/index.js' export * from './src/views/w3m-transactions-view/index.js' export * from './src/views/w3m-what-is-a-network-view/index.js' export * from './src/views/w3m-what-is-a-wallet-view/index.js' diff --git a/packages/scaffold/src/modal/w3m-router/index.ts b/packages/scaffold/src/modal/w3m-router/index.ts index f80d5cf191..7c1a039b87 100644 --- a/packages/scaffold/src/modal/w3m-router/index.ts +++ b/packages/scaffold/src/modal/w3m-router/index.ts @@ -119,6 +119,8 @@ export class W3mRouter extends LitElement { return html`` case 'Swap': return html`` + case 'SwapSelectToken': + return html`` default: return html`` } diff --git a/packages/scaffold/src/partials/w3m-header/index.ts b/packages/scaffold/src/partials/w3m-header/index.ts index f3eca00224..ca1294cb16 100644 --- a/packages/scaffold/src/partials/w3m-header/index.ts +++ b/packages/scaffold/src/partials/w3m-header/index.ts @@ -53,7 +53,8 @@ function headings() { OnRampFiatSelect: 'Select Currency', WalletReceive: 'Receive', WalletCompatibleNetworks: 'Compatible Networks', - Swap: 'Swap' + Swap: 'Swap', + SwapSelectToken: 'Select Token' } } diff --git a/packages/scaffold/src/views/w3m-swap-select-token-view/index.ts b/packages/scaffold/src/views/w3m-swap-select-token-view/index.ts new file mode 100644 index 0000000000..cf0ec568df --- /dev/null +++ b/packages/scaffold/src/views/w3m-swap-select-token-view/index.ts @@ -0,0 +1,227 @@ +import { customElement } from '@web3modal/ui' +import { LitElement, html } from 'lit' +import { state } from 'lit/decorators.js' +import styles from './styles.js' +import { SwapApiController } from '@web3modal/core' + +const yourItems = [ + { + name: 'Ethereum', + symbol: 'ETH', + price: '3,324.34', + value: '0.854' + }, + { + name: 'Avalanche', + symbol: 'AVAX', + price: '2,543.12', + value: '0.723' + }, + { + name: 'Bitcoin', + symbol: 'BTC', + price: '47,892.56', + value: '0.654' + }, + { + name: 'Cardano', + symbol: 'ADA', + price: '2.34', + value: '0.345' + }, + { + name: 'Ripple', + symbol: 'XRP', + price: '1.23', + value: '0.456' + } +] + +const popularItems = [ + { + name: 'Solana', + symbol: 'SOL', + price: '134.56', + value: '0.567' + }, + { + name: 'Polkadot', + symbol: 'DOT', + price: '78.90', + value: '0.678' + }, + { + name: 'Dogecoin', + symbol: 'DOGE', + price: '0.123', + value: '0.789' + }, + { + name: 'Chainlink', + symbol: 'LINK', + price: '23.45', + value: '0.890' + }, + { + name: 'Litecoin', + symbol: 'LTC', + price: '145.67', + value: '0.901' + }, + { + name: 'VeChain', + symbol: 'VET', + price: '0.345', + value: '0.012' + }, + { + name: 'Stellar', + symbol: 'XLM', + price: '0.456', + value: '0.123' + }, + { + name: 'Cosmos', + symbol: 'ATOM', + price: '67.89', + value: '0.234' + }, + { + name: 'Terra', + symbol: 'LUNA', + price: '12.34', + value: '0.345' + }, + { + name: 'Filecoin', + symbol: 'FIL', + price: '234.56', + value: '0.456' + }, + { + name: 'TRON', + symbol: 'TRX', + price: '0.789', + value: '0.567' + }, + { + name: 'Ethereum Classic', + symbol: 'ETC', + price: '45.67', + value: '0.678' + }, + { + name: 'Tezos', + symbol: 'XTZ', + price: '6.78', + value: '0.789' + }, + { + name: 'Monero', + symbol: 'XMR', + price: '123.45', + value: '0.890' + } +] + +@customElement('w3m-swap-select-token-view') +export class W3mSwapSelectTokenView extends LitElement { + public static override styles = styles + + // -- State & Properties -------------------------------- // + @state() private loading = SwapApiController.state.loading + + // -- Lifecycle ----------------------------------------- // + public constructor() { + super() + } + + private getInputElement(el: HTMLElement) { + if (el.shadowRoot?.querySelector('input')) { + return el.shadowRoot.querySelector('input') + } + + return null + } + + private handleInput(e: InputEvent) { + const inputElement = e.target as HTMLElement + const input = this.getInputElement(inputElement) + + if (input) { + const inputValue = input.value + SwapApiController.setSourceTokenAmount(inputValue) + } + } + + private onSwap() { + const amount = SwapApiController.state.sourceTokenAmount + // eslint-disable-next-line no-console + console.log({ amount }) + } + + // -- Render -------------------------------------------- // + public override render() { + return html` + + ${this.templateSearchInput()} ${this.templateListTokens()} + + ` + } + + // -- Private ------------------------------------------- // + private templateSearchInput() { + return html` + + + + ` + } + + private templateListTokens() { + return html` + + + Your tokens + + + ${yourItems.map( + item => html` + + + ` + )} + + + Popular tokens + + + ${popularItems.map( + item => html` + + + ` + )} + + + ` + } +} + +declare global { + interface HTMLElementTagNameMap { + 'w3m-swap-select-token-view': W3mSwapSelectTokenView + } +} diff --git a/packages/scaffold/src/views/w3m-swap-select-token-view/styles.ts b/packages/scaffold/src/views/w3m-swap-select-token-view/styles.ts new file mode 100644 index 0000000000..ff47924c2e --- /dev/null +++ b/packages/scaffold/src/views/w3m-swap-select-token-view/styles.ts @@ -0,0 +1,37 @@ +import { css } from 'lit' + +export default css` + :host > wui-flex:first-child { + overflow-y: hidden; + overflow-x: hidden; + scrollbar-width: none; + } + + wui-loading-hexagon { + position: absolute; + } + + .token-list { + padding-top: var(--wui-spacing-s); + max-height: calc(512px); + overflow-y: auto; + -webkit-mask-image: linear-gradient( + transparent 0px, + transparent 8px, + black 24px, + black 25px, + black 32px, + black 100% + ); + mask-image: linear-gradient( + transparent 0px, + transparent 8px, + black 24px, + black 25px, + black 32px, + black 100% + ); + border-top-left-radius: 30px; + border-top-right-radius: 30px; + } +` diff --git a/packages/scaffold/src/views/w3m-swap-view/index.ts b/packages/scaffold/src/views/w3m-swap-view/index.ts index 492ee1e753..f26a45b691 100644 --- a/packages/scaffold/src/views/w3m-swap-view/index.ts +++ b/packages/scaffold/src/views/w3m-swap-view/index.ts @@ -3,6 +3,11 @@ import { LitElement, html } from 'lit' import { state } from 'lit/decorators.js' import styles from './styles.js' import { SwapApiController } from '@web3modal/core' +import { EventsController } from '@web3modal/core' +import { RouterController } from '@web3modal/core' + +const tokenFrom = 'ETH' +const tokenTo = '' @customElement('w3m-swap-view') export class W3mSwapView extends LitElement { @@ -11,6 +16,8 @@ export class W3mSwapView extends LitElement { // -- State & Properties -------------------------------- // @state() private loading = SwapApiController.state.loading + @state() private networkSrc?: string + // -- Lifecycle ----------------------------------------- // public constructor() { super() @@ -53,27 +60,50 @@ export class W3mSwapView extends LitElement { private templateSwap() { return html` - - Swap tokens - -
${this.templateSwapTokenSelector()}
-
${this.templateSwapTokenSelector()}
+ ${this.templateTokenInput(tokenFrom)} ${this.templateTokenInput(tokenTo)} + ${this.templateReplaceTokensButton()} +
+ + + Enter amount + - Swap
` } + private templateReplaceTokensButton() { + return html` +
+ +
+ ` + } + private templateLoading() { return html`` } - private templateSwapTokenSelector() { - return html` - Amount - this.handleInput(e)} - > - Token - + private templateTokenInput(network: string) { + return html` + + this.handleInput(e)} /> + + ${this.templateTokenSelectButton(network)} ` } + + private templateTokenSelectButton(network: string) { + if (!network) { + return html` + Select token + ` + } + + const networkElement = this.networkSrc + ? html`` + : html` + + ` + + return html` +
+ +
+ ` + } + + private onSelectToken() { + EventsController.sendEvent({ type: 'track', event: 'CLICK_SELECT_TOKEN_TO_SWAP' }) + RouterController.push('SwapSelectToken') + } } declare global { diff --git a/packages/scaffold/src/views/w3m-swap-view/styles.ts b/packages/scaffold/src/views/w3m-swap-view/styles.ts index 125ae89bd0..58cf60eacd 100644 --- a/packages/scaffold/src/views/w3m-swap-view/styles.ts +++ b/packages/scaffold/src/views/w3m-swap-view/styles.ts @@ -2,7 +2,6 @@ import { css } from 'lit' export default css` :host > wui-flex:first-child { - height: 500px; overflow-y: auto; overflow-x: hidden; scrollbar-width: none; @@ -11,4 +10,114 @@ export default css` wui-loading-hexagon { position: absolute; } + + .action-button { + width: 100%; + border-radius: var(--wui-border-radius-xs); + } + + .action-button button { + width: 100%; + height: 48px; + border-radius: var(--wui-border-radius-xs); + } + + .swap-inputs-container { + position: relative; + } + + .replace-tokens-button-container { + display: flex; + justify-content: center; + align-items: center; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background: var(--wui-color-modal-bg); + border-radius: calc(var(--wui-border-radius-s) - 2px); + padding: 5px; + } + + .replace-tokens-button { + display: flex; + justify-content: center; + align-items: center; + gap: var(--4XS, 10px);w + width: 40px; + height: 40px; + padding: var(--5XS, 8px); + border: none; + border-radius: var(--3XS, 12px); + background: var(--wui-gray-glass-005); + } + + .replace-tokens-button:hover { + background: var(--wui-gray-glass-010); + cursor: pointer; + } + + .swap-input { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + background: var(--wui-gray-glass-002); + border: 1px solid var(--wui-gray-glass-002); + border-radius: var(--wui-border-radius-s); + padding: var(--wui-spacing-xl); + padding-right: var(--wui-spacing-s); + width: 100%; + box-sizing: border-box; + position: relative; + } + + .swap-input input { + background: none; + border: none; + height: 48px; + width: 100%; + font-size: 32px; + font-style: normal; + font-weight: 400; + line-height: 130%; + letter-spacing: -1.28px; + outline: none; + } + + .swap-input input:focus-visible { + outline: none; + } + + .swap-input input::-webkit-outer-spin-button, + .swap-input input::-webkit-inner-spin-button { + -webkit-appearance: none; + margin: 0; + } + + .token-select-button-container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + } + + .token-select-button-container button { + display: flex; + align-items: center; + justify-content: center; + gap: var(--wui-spacing-xxs); + padding: var(--wui-spacing-xs); + height: 40px; + border: none; + border-radius: 80px; + background: var(--wui-gray-glass-002); + box-shadow: inset 0 0 0 1px var(--wui-gray-glass-002); + cursor: pointer; + transition: background 0.2s linear; + } + + .token-select-button-container button:hover { + background: var(--wui-gray-glass-005); + } ` diff --git a/packages/ui/index.ts b/packages/ui/index.ts index 5d661225ec..3e9e314408 100644 --- a/packages/ui/index.ts +++ b/packages/ui/index.ts @@ -39,6 +39,7 @@ export * from './src/composites/wui-snackbar/index.js' export * from './src/composites/wui-tabs/index.js' export * from './src/composites/wui-tag/index.js' export * from './src/composites/wui-tooltip/index.js' +export * from './src/composites/wui-token-list-item/index.js' export * from './src/composites/wui-transaction-visual/index.js' export * from './src/composites/wui-visual-thumbnail/index.js' export * from './src/composites/wui-wallet-image/index.js' diff --git a/packages/ui/src/composites/wui-button/styles.ts b/packages/ui/src/composites/wui-button/styles.ts index ce97af3d57..61593b7695 100644 --- a/packages/ui/src/composites/wui-button/styles.ts +++ b/packages/ui/src/composites/wui-button/styles.ts @@ -10,6 +10,7 @@ export default css` border: 1px solid var(--wui-gray-glass-010); border-radius: var(--local-border-radius); width: var(--local-width); + white-space: nowrap; } button:disabled { diff --git a/packages/ui/src/composites/wui-token-list-item/index.ts b/packages/ui/src/composites/wui-token-list-item/index.ts new file mode 100644 index 0000000000..7dd4b51009 --- /dev/null +++ b/packages/ui/src/composites/wui-token-list-item/index.ts @@ -0,0 +1,61 @@ +import { html, LitElement } from 'lit' +import { property } from 'lit/decorators.js' +import '../../components/wui-icon/index.js' +import '../../components/wui-image/index.js' +import '../../components/wui-loading-spinner/index.js' +import '../../components/wui-text/index.js' +import '../../layout/wui-flex/index.js' +import { elementStyles, resetStyles } from '../../utils/ThemeUtil.js' +import { customElement } from '../../utils/WebComponentsUtil.js' +import '../wui-icon-box/index.js' +import styles from './styles.js' + +@customElement('wui-token-list-item') +export class WuiTokenListItem extends LitElement { + public static override styles = [resetStyles, elementStyles, styles] + + // -- State & Properties -------------------------------- // + @property() public imageSrc?: string = undefined + + @property() public name?: string = undefined + + @property() public symbol?: string = undefined + + @property() public price?: string = undefined + + @property() public amount?: string = undefined + + // -- Render -------------------------------------------- // + public override render() { + return html` + + ${this.visualTemplate()} + + + ${this.name} + ${this.price} + + + ${this.symbol} + ${this.amount} + + + + ` + } + + // -- Private ------------------------------------------- // + public visualTemplate() { + if (this.imageSrc) { + return html`` + } + + return null + } +} + +declare global { + interface HTMLElementTagNameMap { + 'wui-token-list-item': WuiTokenListItem + } +} diff --git a/packages/ui/src/composites/wui-token-list-item/styles.ts b/packages/ui/src/composites/wui-token-list-item/styles.ts new file mode 100644 index 0000000000..06e061ebf0 --- /dev/null +++ b/packages/ui/src/composites/wui-token-list-item/styles.ts @@ -0,0 +1,32 @@ +import { css } from 'lit' + +export default css` + :host > wui-flex { + column-gap: var(--wui-spacing-s); + padding: var(--wui-spacing-s); + padding-right: var(--wui-spacing-l); + width: 100%; + background-color: var(--wui-gray-glass-002); + border-radius: var(--wui-border-radius-xs); + color: var(--wui-color-fg-250); + } + + :host > wui-flex > wui-flex { + flex: 1; + } + + :host > wui-flex > wui-image { + width: 40px; + height: 40px; + box-shadow: 0 0 0 2px var(--wui-gray-glass-005); + border-radius: var(--wui-border-radius-3xl); + } + + button > wui-icon-box[data-variant='square-blue'] { + border-radius: var(--wui-border-radius-3xs); + position: relative; + border: none; + width: 36px; + height: 36px; + } +` From fff2914d0664145995e96164e09258217285431c Mon Sep 17 00:00:00 2001 From: enesozturk Date: Wed, 22 Nov 2023 14:17:46 +0300 Subject: [PATCH 03/96] chore: fix ts issues --- .../views/w3m-swap-select-token-view/index.ts | 30 ------------------- 1 file changed, 30 deletions(-) diff --git a/packages/scaffold/src/views/w3m-swap-select-token-view/index.ts b/packages/scaffold/src/views/w3m-swap-select-token-view/index.ts index cf0ec568df..09b956d68e 100644 --- a/packages/scaffold/src/views/w3m-swap-select-token-view/index.ts +++ b/packages/scaffold/src/views/w3m-swap-select-token-view/index.ts @@ -1,8 +1,6 @@ import { customElement } from '@web3modal/ui' import { LitElement, html } from 'lit' -import { state } from 'lit/decorators.js' import styles from './styles.js' -import { SwapApiController } from '@web3modal/core' const yourItems = [ { @@ -127,39 +125,11 @@ const popularItems = [ @customElement('w3m-swap-select-token-view') export class W3mSwapSelectTokenView extends LitElement { public static override styles = styles - - // -- State & Properties -------------------------------- // - @state() private loading = SwapApiController.state.loading - // -- Lifecycle ----------------------------------------- // public constructor() { super() } - private getInputElement(el: HTMLElement) { - if (el.shadowRoot?.querySelector('input')) { - return el.shadowRoot.querySelector('input') - } - - return null - } - - private handleInput(e: InputEvent) { - const inputElement = e.target as HTMLElement - const input = this.getInputElement(inputElement) - - if (input) { - const inputValue = input.value - SwapApiController.setSourceTokenAmount(inputValue) - } - } - - private onSwap() { - const amount = SwapApiController.state.sourceTokenAmount - // eslint-disable-next-line no-console - console.log({ amount }) - } - // -- Render -------------------------------------------- // public override render() { return html` From e3eca530b82ba32f697ba6ccf9764a55b429a854 Mon Sep 17 00:00:00 2001 From: Cali <32299095+Cali93@users.noreply.github.com> Date: Fri, 24 Nov 2023 12:17:44 +0200 Subject: [PATCH 04/96] feat(1inch-api): request data from 1inch swap API (#1497) --- package-lock.json | 934 +++--------------- .../src/controllers/ConnectionController.ts | 17 +- .../core/src/controllers/RouterController.ts | 1 + .../core/src/controllers/SwapApiController.ts | 433 +++++++- packages/core/src/utils/ConstantsUtil.ts | 22 +- packages/core/src/utils/TypeUtil.ts | 10 + .../controllers/ConnectionController.test.ts | 10 +- packages/ethers5/src/client.ts | 33 + .../views/w3m-swap-select-token-view/index.ts | 175 +--- .../scaffold/src/views/w3m-swap-view/index.ts | 198 +++- .../ui/src/composites/wui-input-text/index.ts | 3 +- .../composites/wui-token-list-item/index.ts | 9 +- .../composites/wui-token-list-item/styles.ts | 1 + 13 files changed, 850 insertions(+), 996 deletions(-) diff --git a/package-lock.json b/package-lock.json index 21fc7b55d1..49d8185ddb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -441,663 +441,10 @@ "x-default-browser": "bin/x-default-browser.js" } }, - "node_modules/@aws-crypto/crc32": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-3.0.0.tgz", - "integrity": "sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==", - "dev": true, - "dependencies": { - "@aws-crypto/util": "^3.0.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^1.11.1" - } - }, - "node_modules/@aws-crypto/crc32/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/@aws-crypto/ie11-detection": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz", - "integrity": "sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==", - "dev": true, - "dependencies": { - "tslib": "^1.11.1" - } - }, - "node_modules/@aws-crypto/ie11-detection/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/@aws-crypto/sha256-browser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz", - "integrity": "sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==", - "dev": true, - "dependencies": { - "@aws-crypto/ie11-detection": "^3.0.0", - "@aws-crypto/sha256-js": "^3.0.0", - "@aws-crypto/supports-web-crypto": "^3.0.0", - "@aws-crypto/util": "^3.0.0", - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-locate-window": "^3.0.0", - "@aws-sdk/util-utf8-browser": "^3.0.0", - "tslib": "^1.11.1" - } - }, - "node_modules/@aws-crypto/sha256-browser/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/@aws-crypto/sha256-js": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz", - "integrity": "sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==", - "dev": true, - "dependencies": { - "@aws-crypto/util": "^3.0.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^1.11.1" - } - }, - "node_modules/@aws-crypto/sha256-js/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/@aws-crypto/supports-web-crypto": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz", - "integrity": "sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==", - "dev": true, - "dependencies": { - "tslib": "^1.11.1" - } - }, - "node_modules/@aws-crypto/supports-web-crypto/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/@aws-crypto/util": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", - "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", - "dev": true, - "dependencies": { - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-utf8-browser": "^3.0.0", - "tslib": "^1.11.1" - } - }, - "node_modules/@aws-crypto/util/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/@aws-sdk/client-cloudwatch": { - "version": "3.509.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-cloudwatch/-/client-cloudwatch-3.509.0.tgz", - "integrity": "sha512-qWclNb0gOA58DxvYOr+omlkMn8xvg5FoKxvnGUBifJuGf0J2TOv74Y9u2LGm1FCVJskuw1TXnfxzeHpbMJyDyw==", - "dev": true, - "dependencies": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/client-sts": "3.507.0", - "@aws-sdk/core": "3.496.0", - "@aws-sdk/credential-provider-node": "3.509.0", - "@aws-sdk/middleware-host-header": "3.502.0", - "@aws-sdk/middleware-logger": "3.502.0", - "@aws-sdk/middleware-recursion-detection": "3.502.0", - "@aws-sdk/middleware-signing": "3.502.0", - "@aws-sdk/middleware-user-agent": "3.502.0", - "@aws-sdk/region-config-resolver": "3.502.0", - "@aws-sdk/types": "3.502.0", - "@aws-sdk/util-endpoints": "3.502.0", - "@aws-sdk/util-user-agent-browser": "3.502.0", - "@aws-sdk/util-user-agent-node": "3.502.0", - "@smithy/config-resolver": "^2.1.1", - "@smithy/core": "^1.3.1", - "@smithy/fetch-http-handler": "^2.4.1", - "@smithy/hash-node": "^2.1.1", - "@smithy/invalid-dependency": "^2.1.1", - "@smithy/middleware-content-length": "^2.1.1", - "@smithy/middleware-endpoint": "^2.4.1", - "@smithy/middleware-retry": "^2.1.1", - "@smithy/middleware-serde": "^2.1.1", - "@smithy/middleware-stack": "^2.1.1", - "@smithy/node-config-provider": "^2.2.1", - "@smithy/node-http-handler": "^2.3.1", - "@smithy/protocol-http": "^3.1.1", - "@smithy/smithy-client": "^2.3.1", - "@smithy/types": "^2.9.1", - "@smithy/url-parser": "^2.1.1", - "@smithy/util-base64": "^2.1.1", - "@smithy/util-body-length-browser": "^2.1.1", - "@smithy/util-body-length-node": "^2.2.1", - "@smithy/util-defaults-mode-browser": "^2.1.1", - "@smithy/util-defaults-mode-node": "^2.1.1", - "@smithy/util-endpoints": "^1.1.1", - "@smithy/util-retry": "^2.1.1", - "@smithy/util-utf8": "^2.1.1", - "@smithy/util-waiter": "^2.1.1", - "fast-xml-parser": "4.2.5", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-sso": { - "version": "3.507.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.507.0.tgz", - "integrity": "sha512-pFeaKwqv4tXD6QVxWC2V4N62DUoP3bPSm/mCe2SPhaNjNsmwwA53viUHz/nwxIbs8w4vV44UQsygb0AgKm+HoQ==", - "dev": true, - "dependencies": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/core": "3.496.0", - "@aws-sdk/middleware-host-header": "3.502.0", - "@aws-sdk/middleware-logger": "3.502.0", - "@aws-sdk/middleware-recursion-detection": "3.502.0", - "@aws-sdk/middleware-user-agent": "3.502.0", - "@aws-sdk/region-config-resolver": "3.502.0", - "@aws-sdk/types": "3.502.0", - "@aws-sdk/util-endpoints": "3.502.0", - "@aws-sdk/util-user-agent-browser": "3.502.0", - "@aws-sdk/util-user-agent-node": "3.502.0", - "@smithy/config-resolver": "^2.1.1", - "@smithy/core": "^1.3.1", - "@smithy/fetch-http-handler": "^2.4.1", - "@smithy/hash-node": "^2.1.1", - "@smithy/invalid-dependency": "^2.1.1", - "@smithy/middleware-content-length": "^2.1.1", - "@smithy/middleware-endpoint": "^2.4.1", - "@smithy/middleware-retry": "^2.1.1", - "@smithy/middleware-serde": "^2.1.1", - "@smithy/middleware-stack": "^2.1.1", - "@smithy/node-config-provider": "^2.2.1", - "@smithy/node-http-handler": "^2.3.1", - "@smithy/protocol-http": "^3.1.1", - "@smithy/smithy-client": "^2.3.1", - "@smithy/types": "^2.9.1", - "@smithy/url-parser": "^2.1.1", - "@smithy/util-base64": "^2.1.1", - "@smithy/util-body-length-browser": "^2.1.1", - "@smithy/util-body-length-node": "^2.2.1", - "@smithy/util-defaults-mode-browser": "^2.1.1", - "@smithy/util-defaults-mode-node": "^2.1.1", - "@smithy/util-endpoints": "^1.1.1", - "@smithy/util-retry": "^2.1.1", - "@smithy/util-utf8": "^2.1.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc": { - "version": "3.507.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.507.0.tgz", - "integrity": "sha512-ms5CH2ImhqqCIbo5irxayByuPOlVAmSiqDVfjZKwgIziqng2bVgNZMeKcT6t0bmrcgScEAVnZwY7j/iZTIw73g==", - "dev": true, - "dependencies": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/client-sts": "3.507.0", - "@aws-sdk/core": "3.496.0", - "@aws-sdk/middleware-host-header": "3.502.0", - "@aws-sdk/middleware-logger": "3.502.0", - "@aws-sdk/middleware-recursion-detection": "3.502.0", - "@aws-sdk/middleware-signing": "3.502.0", - "@aws-sdk/middleware-user-agent": "3.502.0", - "@aws-sdk/region-config-resolver": "3.502.0", - "@aws-sdk/types": "3.502.0", - "@aws-sdk/util-endpoints": "3.502.0", - "@aws-sdk/util-user-agent-browser": "3.502.0", - "@aws-sdk/util-user-agent-node": "3.502.0", - "@smithy/config-resolver": "^2.1.1", - "@smithy/core": "^1.3.1", - "@smithy/fetch-http-handler": "^2.4.1", - "@smithy/hash-node": "^2.1.1", - "@smithy/invalid-dependency": "^2.1.1", - "@smithy/middleware-content-length": "^2.1.1", - "@smithy/middleware-endpoint": "^2.4.1", - "@smithy/middleware-retry": "^2.1.1", - "@smithy/middleware-serde": "^2.1.1", - "@smithy/middleware-stack": "^2.1.1", - "@smithy/node-config-provider": "^2.2.1", - "@smithy/node-http-handler": "^2.3.1", - "@smithy/protocol-http": "^3.1.1", - "@smithy/smithy-client": "^2.3.1", - "@smithy/types": "^2.9.1", - "@smithy/url-parser": "^2.1.1", - "@smithy/util-base64": "^2.1.1", - "@smithy/util-body-length-browser": "^2.1.1", - "@smithy/util-body-length-node": "^2.2.1", - "@smithy/util-defaults-mode-browser": "^2.1.1", - "@smithy/util-defaults-mode-node": "^2.1.1", - "@smithy/util-endpoints": "^1.1.1", - "@smithy/util-retry": "^2.1.1", - "@smithy/util-utf8": "^2.1.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "@aws-sdk/credential-provider-node": "^3.507.0" - } - }, - "node_modules/@aws-sdk/client-sts": { - "version": "3.507.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.507.0.tgz", - "integrity": "sha512-TOWBe0ApEh32QOib0R+irWGjd1F9wnhbGV5PcB9SakyRwvqwG5MKOfYxG7ocoDqLlaRwzZMidcy/PV8/OEVNKg==", - "dev": true, - "dependencies": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/core": "3.496.0", - "@aws-sdk/middleware-host-header": "3.502.0", - "@aws-sdk/middleware-logger": "3.502.0", - "@aws-sdk/middleware-recursion-detection": "3.502.0", - "@aws-sdk/middleware-user-agent": "3.502.0", - "@aws-sdk/region-config-resolver": "3.502.0", - "@aws-sdk/types": "3.502.0", - "@aws-sdk/util-endpoints": "3.502.0", - "@aws-sdk/util-user-agent-browser": "3.502.0", - "@aws-sdk/util-user-agent-node": "3.502.0", - "@smithy/config-resolver": "^2.1.1", - "@smithy/core": "^1.3.1", - "@smithy/fetch-http-handler": "^2.4.1", - "@smithy/hash-node": "^2.1.1", - "@smithy/invalid-dependency": "^2.1.1", - "@smithy/middleware-content-length": "^2.1.1", - "@smithy/middleware-endpoint": "^2.4.1", - "@smithy/middleware-retry": "^2.1.1", - "@smithy/middleware-serde": "^2.1.1", - "@smithy/middleware-stack": "^2.1.1", - "@smithy/node-config-provider": "^2.2.1", - "@smithy/node-http-handler": "^2.3.1", - "@smithy/protocol-http": "^3.1.1", - "@smithy/smithy-client": "^2.3.1", - "@smithy/types": "^2.9.1", - "@smithy/url-parser": "^2.1.1", - "@smithy/util-base64": "^2.1.1", - "@smithy/util-body-length-browser": "^2.1.1", - "@smithy/util-body-length-node": "^2.2.1", - "@smithy/util-defaults-mode-browser": "^2.1.1", - "@smithy/util-defaults-mode-node": "^2.1.1", - "@smithy/util-endpoints": "^1.1.1", - "@smithy/util-middleware": "^2.1.1", - "@smithy/util-retry": "^2.1.1", - "@smithy/util-utf8": "^2.1.1", - "fast-xml-parser": "4.2.5", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "@aws-sdk/credential-provider-node": "^3.507.0" - } - }, - "node_modules/@aws-sdk/core": { - "version": "3.496.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.496.0.tgz", - "integrity": "sha512-yT+ug7Cw/3eJi7x2es0+46x12+cIJm5Xv+GPWsrTFD1TKgqO/VPEgfDtHFagDNbFmjNQA65Ygc/kEdIX9ICX/A==", - "dev": true, - "dependencies": { - "@smithy/core": "^1.3.1", - "@smithy/protocol-http": "^3.1.1", - "@smithy/signature-v4": "^2.1.1", - "@smithy/smithy-client": "^2.3.1", - "@smithy/types": "^2.9.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.502.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.502.0.tgz", - "integrity": "sha512-KIB8Ae1Z7domMU/jU4KiIgK4tmYgvuXlhR54ehwlVHxnEoFPoPuGHFZU7oFn79jhhSLUFQ1lRYMxP0cEwb7XeQ==", - "dev": true, - "dependencies": { - "@aws-sdk/types": "3.502.0", - "@smithy/property-provider": "^2.1.1", - "@smithy/types": "^2.9.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.503.1", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.503.1.tgz", - "integrity": "sha512-rTdlFFGoPPFMF2YjtlfRuSgKI+XsF49u7d98255hySwhsbwd3Xp+utTTPquxP+CwDxMHbDlI7NxDzFiFdsoZug==", - "dev": true, - "dependencies": { - "@aws-sdk/types": "3.502.0", - "@smithy/fetch-http-handler": "^2.4.1", - "@smithy/node-http-handler": "^2.3.1", - "@smithy/property-provider": "^2.1.1", - "@smithy/protocol-http": "^3.1.1", - "@smithy/smithy-client": "^2.3.1", - "@smithy/types": "^2.9.1", - "@smithy/util-stream": "^2.1.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.507.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.507.0.tgz", - "integrity": "sha512-2CnyduoR9COgd7qH1LPYK8UggGqVs8R4ASDMB5bwGxbg9ZerlStDiHpqvJNNg1k+VlejBr++utxfmHd236XgmQ==", - "dev": true, - "dependencies": { - "@aws-sdk/client-sts": "3.507.0", - "@aws-sdk/credential-provider-env": "3.502.0", - "@aws-sdk/credential-provider-process": "3.502.0", - "@aws-sdk/credential-provider-sso": "3.507.0", - "@aws-sdk/credential-provider-web-identity": "3.507.0", - "@aws-sdk/types": "3.502.0", - "@smithy/credential-provider-imds": "^2.2.1", - "@smithy/property-provider": "^2.1.1", - "@smithy/shared-ini-file-loader": "^2.3.1", - "@smithy/types": "^2.9.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.509.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.509.0.tgz", - "integrity": "sha512-uXT8wIq1k+m0mS/pC9U1cUTIjUB7/4PgxyiYsTxYPIULtWnQXltAlcPU3QzKTJMP60sqftRYZ2jFDLAVsipQxw==", - "dev": true, - "dependencies": { - "@aws-sdk/credential-provider-env": "3.502.0", - "@aws-sdk/credential-provider-http": "3.503.1", - "@aws-sdk/credential-provider-ini": "3.507.0", - "@aws-sdk/credential-provider-process": "3.502.0", - "@aws-sdk/credential-provider-sso": "3.507.0", - "@aws-sdk/credential-provider-web-identity": "3.507.0", - "@aws-sdk/types": "3.502.0", - "@smithy/credential-provider-imds": "^2.2.1", - "@smithy/property-provider": "^2.1.1", - "@smithy/shared-ini-file-loader": "^2.3.1", - "@smithy/types": "^2.9.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.502.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.502.0.tgz", - "integrity": "sha512-fJJowOjQ4infYQX0E1J3xFVlmuwEYJAFk0Mo1qwafWmEthsBJs+6BR2RiWDELHKrSK35u4Pf3fu3RkYuCtmQFw==", - "dev": true, - "dependencies": { - "@aws-sdk/types": "3.502.0", - "@smithy/property-provider": "^2.1.1", - "@smithy/shared-ini-file-loader": "^2.3.1", - "@smithy/types": "^2.9.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.507.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.507.0.tgz", - "integrity": "sha512-6WBjou52QukFpDi4ezb19bcAx/bM8ge8qnJnRT02WVRmU6zFQ5yLD2fW1MFsbX3cwbey+wSqKd5FGE1Hukd5wQ==", - "dev": true, - "dependencies": { - "@aws-sdk/client-sso": "3.507.0", - "@aws-sdk/token-providers": "3.507.0", - "@aws-sdk/types": "3.502.0", - "@smithy/property-provider": "^2.1.1", - "@smithy/shared-ini-file-loader": "^2.3.1", - "@smithy/types": "^2.9.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.507.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.507.0.tgz", - "integrity": "sha512-f+aGMfazBimX7S06224JRYzGTaMh1uIhfj23tZylPJ05KxTVi5IO1RoqeI/uHLJ+bDOx+JHBC04g/oCdO4kHvw==", - "dev": true, - "dependencies": { - "@aws-sdk/client-sts": "3.507.0", - "@aws-sdk/types": "3.502.0", - "@smithy/property-provider": "^2.1.1", - "@smithy/types": "^2.9.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/middleware-host-header": { - "version": "3.502.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.502.0.tgz", - "integrity": "sha512-EjnG0GTYXT/wJBmm5/mTjDcAkzU8L7wQjOzd3FTXuTCNNyvAvwrszbOj5FlarEw5XJBbQiZtBs+I5u9+zy560w==", - "dev": true, - "dependencies": { - "@aws-sdk/types": "3.502.0", - "@smithy/protocol-http": "^3.1.1", - "@smithy/types": "^2.9.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/middleware-logger": { - "version": "3.502.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.502.0.tgz", - "integrity": "sha512-FDyv6K4nCoHxbjLGS2H8ex8I0KDIiu4FJgVRPs140ZJy6gE5Pwxzv6YTzZGLMrnqcIs9gh065Lf6DjwMelZqaw==", - "dev": true, - "dependencies": { - "@aws-sdk/types": "3.502.0", - "@smithy/types": "^2.9.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.502.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.502.0.tgz", - "integrity": "sha512-hvbyGJbxeuezxOu8VfFmcV4ql1hKXLxHTe5FNYfEBat2KaZXVhc1Hg+4TvB06/53p+E8J99Afmumkqbxs2esUA==", - "dev": true, - "dependencies": { - "@aws-sdk/types": "3.502.0", - "@smithy/protocol-http": "^3.1.1", - "@smithy/types": "^2.9.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/middleware-signing": { - "version": "3.502.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.502.0.tgz", - "integrity": "sha512-4hF08vSzJ7L6sB+393gOFj3s2N6nLusYS0XrMW6wYNFU10IDdbf8Z3TZ7gysDJJHEGQPmTAesPEDBsasGWcMxg==", - "dev": true, - "dependencies": { - "@aws-sdk/types": "3.502.0", - "@smithy/property-provider": "^2.1.1", - "@smithy/protocol-http": "^3.1.1", - "@smithy/signature-v4": "^2.1.1", - "@smithy/types": "^2.9.1", - "@smithy/util-middleware": "^2.1.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.502.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.502.0.tgz", - "integrity": "sha512-TxbBZbRiXPH0AUxegqiNd9aM9zNSbfjtBs5MEfcBsweeT/B2O7K1EjP9+CkB8Xmk/5FLKhAKLr19b1TNoE27rw==", - "dev": true, - "dependencies": { - "@aws-sdk/types": "3.502.0", - "@aws-sdk/util-endpoints": "3.502.0", - "@smithy/protocol-http": "^3.1.1", - "@smithy/types": "^2.9.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/region-config-resolver": { - "version": "3.502.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.502.0.tgz", - "integrity": "sha512-mxmsX2AGgnSM+Sah7mcQCIneOsJQNiLX0COwEttuf8eO+6cLMAZvVudH3BnWTfea4/A9nuri9DLCqBvEmPrilg==", - "dev": true, - "dependencies": { - "@aws-sdk/types": "3.502.0", - "@smithy/node-config-provider": "^2.2.1", - "@smithy/types": "^2.9.1", - "@smithy/util-config-provider": "^2.2.1", - "@smithy/util-middleware": "^2.1.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/token-providers": { - "version": "3.507.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.507.0.tgz", - "integrity": "sha512-ehOINGjoGJc6Puzon7ev4bXckkaZx18WNgMTNttYJhj3vTpj5LPSQbI/5SS927bEbpGMFz1+hJ6Ra5WGfbTcEQ==", - "dev": true, - "dependencies": { - "@aws-sdk/client-sso-oidc": "3.507.0", - "@aws-sdk/types": "3.502.0", - "@smithy/property-provider": "^2.1.1", - "@smithy/shared-ini-file-loader": "^2.3.1", - "@smithy/types": "^2.9.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/types": { - "version": "3.502.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.502.0.tgz", - "integrity": "sha512-M0DSPYe/gXhwD2QHgoukaZv5oDxhW3FfvYIrJptyqUq3OnPJBcDbihHjrE0PBtfh/9kgMZT60/fQ2NVFANfa2g==", - "dev": true, - "dependencies": { - "@smithy/types": "^2.9.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/util-endpoints": { - "version": "3.502.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.502.0.tgz", - "integrity": "sha512-6LKFlJPp2J24r1Kpfoz5ESQn+1v5fEjDB3mtUKRdpwarhm3syu7HbKlHCF3KbcCOyahobvLvhoedT78rJFEeeg==", - "dev": true, - "dependencies": { - "@aws-sdk/types": "3.502.0", - "@smithy/types": "^2.9.1", - "@smithy/util-endpoints": "^1.1.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/util-locate-window": { - "version": "3.495.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.495.0.tgz", - "integrity": "sha512-MfaPXT0kLX2tQaR90saBT9fWQq2DHqSSJRzW+MZWsmF+y5LGCOhO22ac/2o6TKSQm7h0HRc2GaADqYYYor62yg==", - "dev": true, - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.502.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.502.0.tgz", - "integrity": "sha512-v8gKyCs2obXoIkLETAeEQ3AM+QmhHhst9xbM1cJtKUGsRlVIak/XyyD+kVE6kmMm1cjfudHpHKABWk9apQcIZQ==", - "dev": true, - "dependencies": { - "@aws-sdk/types": "3.502.0", - "@smithy/types": "^2.9.1", - "bowser": "^2.11.0", - "tslib": "^2.5.0" - } - }, - "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.502.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.502.0.tgz", - "integrity": "sha512-9RjxpkGZKbTdl96tIJvAo+vZoz4P/cQh36SBUt9xfRfW0BtsaLyvSrvlR5wyUYhvRcC12Axqh/8JtnAPq//+Vw==", - "dev": true, - "dependencies": { - "@aws-sdk/types": "3.502.0", - "@smithy/node-config-provider": "^2.2.1", - "@smithy/types": "^2.9.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "aws-crt": ">=1.0.0" - }, - "peerDependenciesMeta": { - "aws-crt": { - "optional": true - } - } - }, - "node_modules/@aws-sdk/util-utf8-browser": { - "version": "3.259.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", - "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", - "dev": true, - "dependencies": { - "tslib": "^2.3.1" - } - }, "node_modules/@babel/code-frame": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", - "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.4.tgz", + "integrity": "sha512-r1IONyb6Ia+jYR2vvIDhdWdlTGhqbBoFqLTQidzZ4kepUFH15ejXvFHxCVbtl7BOXIudsIubf4E81xeA3h3IXA==", "dependencies": { "@babel/highlight": "^7.23.4", "chalk": "^2.4.2" @@ -1157,11 +504,11 @@ } }, "node_modules/@babel/generator": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", - "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.4.tgz", + "integrity": "sha512-esuS49Cga3HcThFNebGhlgsrVLkvhqvYDTzgjfFFlHJcIfLe5jFmRRfCQ1KuBfc4Jrtn3ndLgKWAKjBE+IraYQ==", "dependencies": { - "@babel/types": "^7.23.6", + "@babel/types": "^7.23.4", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -1477,13 +824,13 @@ } }, "node_modules/@babel/helpers": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.0.tgz", - "integrity": "sha512-ulDZdc0Aj5uLc5nETsa7EPx2L7rM0YJM8r7ck7U73AXi7qOV44IHHRAYZHY6iU1rr3C5N4NtTmMRUJP6kwCWeA==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.4.tgz", + "integrity": "sha512-HfcMizYz10cr3h29VqyfGL6ZWIjTwWfvYBMsBVGwpcbhNGe3wQ1ZXZRPzZoAHhd9OqHadHqjQ89iVKINXnbzuw==", "dependencies": { - "@babel/template": "^7.24.0", - "@babel/traverse": "^7.24.0", - "@babel/types": "^7.24.0" + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.4", + "@babel/types": "^7.23.4" }, "engines": { "node": ">=6.9.0" @@ -1503,9 +850,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.0.tgz", - "integrity": "sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.4.tgz", + "integrity": "sha512-vf3Xna6UEprW+7t6EtOmFpHNAuxw3xqPZghy+brsnusscJRW5BMUzzHZc5ICjULee81WeUV2jjakG09MDglJXQ==", "bin": { "parser": "bin/babel-parser.js" }, @@ -1999,9 +1346,9 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.9.tgz", - "integrity": "sha512-8Q3veQEDGe14dTYuwagbRtwxQDnytyg1JFu4/HwEMETeofocrB0U0ejBJIXoeG/t2oXZ8kzCyI0ZZfbT80VFNQ==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.4.tgz", + "integrity": "sha512-efdkfPhHYTtn0G6n2ddrESE91fgXxjlqLsnUtPWnJs4a4mZIbUaK7ffqKIIUKXSHwcDvaCVX6GXkaJJFqtX7jw==", "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-plugin-utils": "^7.22.5", @@ -2756,9 +2103,9 @@ } }, "node_modules/@babel/plugin-transform-typescript": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.23.6.tgz", - "integrity": "sha512-6cBG5mBvUu4VUD04OHKnYzbuHNP8huDsD3EDqqpIpsswTDoqHCjLoHb6+QgsV1WsT2nipRqCPgxD3LXnEO7XfA==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.23.4.tgz", + "integrity": "sha512-39hCCOl+YUAyMOu6B9SmUTiHUU0t/CxJNUmY3qRdJujbqi+lrQcL11ysYUsAvFWPBdhihrv1z0oRG84Yr3dODQ==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-create-class-features-plugin": "^7.23.6", @@ -3097,9 +2444,9 @@ "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" }, "node_modules/@babel/runtime": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.0.tgz", - "integrity": "sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.4.tgz", + "integrity": "sha512-2Yv65nlWnWlSpe3fXEyX5i7fx5kIKo4Qbcj+hMO0odwaneFjfXw5fdum+4yL20O0QiaHpia0cYQ9xpNMqrBwHg==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -3121,19 +2468,19 @@ } }, "node_modules/@babel/traverse": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.0.tgz", - "integrity": "sha512-HfuJlI8qq3dEDmNU5ChzzpZRWq+oxCZQyMzIMEqLho+AQnhMnKQUzH6ydo3RBl/YjPCuk68Y6s0Gx0AeyULiWw==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.4.tgz", + "integrity": "sha512-IYM8wSUwunWTB6tFC2dkKZhxbIjHoWemdK+3f8/wq8aKhbUscxD5MX72ubd90fxvFknaLPeGw5ycU84V1obHJg==", "dependencies": { - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.6", + "@babel/code-frame": "^7.23.4", + "@babel/generator": "^7.23.4", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.24.0", - "@babel/types": "^7.24.0", - "debug": "^4.3.1", + "@babel/parser": "^7.23.4", + "@babel/types": "^7.23.4", + "debug": "^4.1.0", "globals": "^11.1.0" }, "engines": { @@ -3141,9 +2488,9 @@ } }, "node_modules/@babel/types": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", - "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.4.tgz", + "integrity": "sha512-7uIFwVYpoplT5jp/kVv6EF93VaJ8H+Yn5IczYiaAi98ajzjfoZfslet/e0sLh+wVBjb2qqIut1b0S26VSafsSQ==", "dependencies": { "@babel/helper-string-parser": "^7.23.4", "@babel/helper-validator-identifier": "^7.22.20", @@ -6503,9 +5850,9 @@ "integrity": "sha512-yWJKmpGE6lUURKAaIltoPIE/wrbY3TEkqQt+X0m+7fQNnAv0keydnYvbiJFP1PnMhizmIWRWOG5KLhYyc/xl+g==" }, "node_modules/@lit/reactive-element": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.0.4.tgz", - "integrity": "sha512-GFn91inaUa2oHLak8awSIigYz0cU0Payr1rcFsrkf5OJ5eSPxElyZfKh0f2p9FsTiZWXQdWGJeXZICEfXXYSXQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.0.2.tgz", + "integrity": "sha512-SVOwLAWUQg3Ji1egtOt1UiFe4zdDpnWHyc5qctSceJ5XIu0Uc76YmGpIjZgx9YJ0XtdW0Jm507sDvjOu+HnB8w==", "dependencies": { "@lit-labs/ssr-dom-shim": "^1.2.0" } @@ -11950,9 +11297,9 @@ } }, "node_modules/@storybook/core-common/node_modules/@types/node": { - "version": "18.19.22", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.22.tgz", - "integrity": "sha512-p3pDIfuMg/aXBmhkyanPshdfJuX5c5+bQjYLIikPLXAUycEogij/c50n/C+8XOA5L93cU4ZRXtn+dNQGi0IZqQ==", + "version": "18.18.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.12.tgz", + "integrity": "sha512-G7slVfkwOm7g8VqcEF1/5SXiMjP3Tbt+pXDU3r/qhlM2KkGm786DUD4xyMA2QzEElFrv/KZV9gjygv4LnkpbMQ==", "dependencies": { "undici-types": "~5.26.4" } @@ -12181,9 +11528,9 @@ } }, "node_modules/@storybook/core-server/node_modules/@types/node": { - "version": "18.19.22", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.22.tgz", - "integrity": "sha512-p3pDIfuMg/aXBmhkyanPshdfJuX5c5+bQjYLIikPLXAUycEogij/c50n/C+8XOA5L93cU4ZRXtn+dNQGi0IZqQ==", + "version": "18.18.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.12.tgz", + "integrity": "sha512-G7slVfkwOm7g8VqcEF1/5SXiMjP3Tbt+pXDU3r/qhlM2KkGm786DUD4xyMA2QzEElFrv/KZV9gjygv4LnkpbMQ==", "dependencies": { "undici-types": "~5.26.4" } @@ -12848,10 +12195,17 @@ "@types/responselike": "^1.0.0" } }, - "node_modules/@types/chrome": { - "version": "0.0.136", - "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.136.tgz", - "integrity": "sha512-XDEiRhLkMd+SB7Iw3ZUIj/fov3wLd4HyTdLltVszkgl1dBfc3Rb7oPMVZ2Mz2TLqnF7Ow+StbR8E7r9lqpb4DA==", + "node_modules/@types/chai": { + "version": "4.3.11", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.11.tgz", + "integrity": "sha512-qQR1dr2rGIHYlJulmr8Ioq3De0Le9E4MJ5AiaeAETJJpndT1uUNHsGFK3L/UIu+rbkQSdj8J/w2bCsBZc/Y5fQ==", + "dev": true + }, + "node_modules/@types/chai-subset": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/chai-subset/-/chai-subset-1.3.5.tgz", + "integrity": "sha512-c2mPnw+xHtXDoHmdtcCXGwyLMiauiAyxWMzhGpqHC4nqI/Y5G2XhTampslK2rb59kpcuHon03UH8W6iYUzw88A==", + "dev": true, "dependencies": { "@types/filesystem": "*", "@types/har-format": "*" @@ -13080,9 +12434,9 @@ "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" }, "node_modules/@types/node": { - "version": "20.11.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.5.tgz", - "integrity": "sha512-g557vgQjUUfN76MZAN/dt1z3dzcUsimuysco0KeluHgrPdJXkP/XdAURgyO2W9fZWHRtRBiVKzKn8vyOAwlG+w==", + "version": "20.9.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.4.tgz", + "integrity": "sha512-wmyg8HUhcn6ACjsn8oKYjkN/zUzQeNtMy44weTJSM6p4MMzEOuKbA3OjJ267uPCOW7Xex9dyrNTful8XTQYoDA==", "dependencies": { "undici-types": "~5.26.4" } @@ -13189,9 +12543,9 @@ } }, "node_modules/@types/semver": { - "version": "7.5.8", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", - "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==" + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", + "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==" }, "node_modules/@types/send": { "version": "0.17.4", @@ -13239,6 +12593,7 @@ "version": "17.0.32", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", + "dev": true, "dependencies": { "@types/yargs-parser": "*" } @@ -14943,9 +14298,9 @@ } }, "node_modules/axios": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz", - "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", + "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", "dev": true, "dependencies": { "follow-redirects": "^1.15.4", @@ -15129,11 +14484,19 @@ "node": ">=12.0.0" } }, - "node_modules/better-path-resolve": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/better-path-resolve/-/better-path-resolve-1.0.0.tgz", - "integrity": "sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==", - "dev": true, + "node_modules/big-integer": { + "version": "1.6.52", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", + "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/bigint-buffer": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/bigint-buffer/-/bigint-buffer-1.1.5.tgz", + "integrity": "sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA==", + "hasInstallScript": true, "dependencies": { "is-windows": "^1.0.0" }, @@ -15585,9 +14948,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001597", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001597.tgz", - "integrity": "sha512-7LjJvmQU6Sj7bL0j5b5WY/3n7utXUJvAe1lxhsHDbLmwX9mdL86Yjtr+5SRCyf8qME4M7pU2hswj0FpyBVCv9w==", + "version": "1.0.30001564", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001564.tgz", + "integrity": "sha512-DqAOf+rhof+6GVx1y+xzbFPeOumfQnhYzVnZD6LAXijR77yPtm9mfOcqOnT3mpnJiZVT+kwLAFnRlZcIz+c6bg==", "funding": [ { "type": "opencollective", @@ -15761,9 +15124,9 @@ } }, "node_modules/citty": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", - "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.5.tgz", + "integrity": "sha512-AS7n5NSc0OQVMV9v6wt3ByujNIrne0/cTjiC2MYqhvao57VNfiuVksTSr2p17nVOhEr2KtqiAkGwHcgMC/qUuQ==", "dependencies": { "consola": "^3.2.3" } @@ -16343,9 +15706,9 @@ } }, "node_modules/core-js": { - "version": "3.36.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.36.0.tgz", - "integrity": "sha512-mt7+TUBbTFg5+GngsAxeKBTl5/VS0guFeJacYge9OmHb+m058UwwIm41SE9T4Den7ClatV57B6TYTuJ0CX1MAw==", + "version": "3.33.3", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.33.3.tgz", + "integrity": "sha512-lo0kOocUlLKmm6kv/FswQL8zbkH7mVsLJ/FULClOhv8WRVmKLVcs6XPNQAzstfeJTCHMyButEwG+z1kHxHoDZw==", "dev": true, "hasInstallScript": true, "funding": { @@ -16354,9 +15717,9 @@ } }, "node_modules/core-js-compat": { - "version": "3.36.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.36.0.tgz", - "integrity": "sha512-iV9Pd/PsgjNWBXeq8XRtWVSgz2tKAfhfvBs7qxYty+RlRd+OCksaWmOnc4JKrTc1cToXL1N0s3l/vwlxPtdElw==", + "version": "3.33.3", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.33.3.tgz", + "integrity": "sha512-cNzGqFsh3Ot+529GIXacjTJ7kegdt5fPXxCBVS1G0iaZpuo/tBz399ymceLJveQhFFZ8qThHiP3fzuoQjKN2ow==", "dependencies": { "browserslist": "^4.22.3" }, @@ -17068,9 +16431,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.699", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.699.tgz", - "integrity": "sha512-I7q3BbQi6e4tJJN5CRcyvxhK0iJb34TV8eJQcgh+fR2fQ8miMgZcEInckCo1U9exDHbfz7DLDnFn8oqH/VcRKw==" + "version": "1.4.590", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.590.tgz", + "integrity": "sha512-hohItzsQcG7/FBsviCYMtQwUSWvVF7NVqPOnJCErWsAshsP/CR2LAXdmq276RbESNdhxiAq5/vRo1g2pxGXVww==" }, "node_modules/elliptic": { "version": "6.5.4", @@ -18930,9 +18293,9 @@ "peer": true }, "node_modules/flow-parser": { - "version": "0.206.0", - "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.206.0.tgz", - "integrity": "sha512-HVzoK3r6Vsg+lKvlIZzaWNBVai+FXTX1wdYhz/wVlH13tb/gOdLXmlTqy6odmTBhT5UoWUbq0k8263Qhr9d88w==", + "version": "0.222.0", + "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.222.0.tgz", + "integrity": "sha512-Fq5OkFlFRSMV2EOZW+4qUYMTE0uj8pcLsYJMxXYriSBDpHAF7Ofx3PibCTy3cs5P6vbsry7eYj7Z7xFD49GIOQ==", "engines": { "node": ">=0.4.0" } @@ -19519,18 +18882,16 @@ } }, "node_modules/h3": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/h3/-/h3-1.11.1.tgz", - "integrity": "sha512-AbaH6IDnZN6nmbnJOH72y3c5Wwh9P97soSVdGSBbcDACRdkC0FEWf25pzx4f/NuOCK6quHmW18yF2Wx+G4Zi1A==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/h3/-/h3-1.9.0.tgz", + "integrity": "sha512-+F3ZqrNV/CFXXfZ2lXBINHi+rM4Xw3CDC5z2CDK3NMPocjonKipGLLDSkrqY9DOrioZNPTIdDMWfQKm//3X2DA==", "dependencies": { "cookie-es": "^1.0.0", - "crossws": "^0.2.2", - "defu": "^6.1.4", - "destr": "^2.0.3", + "defu": "^6.1.3", + "destr": "^2.0.2", "iron-webcrypto": "^1.0.0", - "ohash": "^1.1.3", "radix3": "^1.1.0", - "ufo": "^1.4.0", + "ufo": "^1.3.2", "uncrypto": "^0.1.3", "unenv": "^1.9.0" } @@ -19906,9 +19267,9 @@ ] }, "node_modules/ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", + "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", "engines": { "node": ">= 4" } @@ -21866,19 +21227,19 @@ } }, "node_modules/lit-element": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-4.0.4.tgz", - "integrity": "sha512-98CvgulX6eCPs6TyAIQoJZBCQPo80rgXR+dVBs61cstJXqtI+USQZAbA4gFHh6L/mxBx9MrgPLHLsUgDUHAcCQ==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-4.0.2.tgz", + "integrity": "sha512-/W6WQZUa5VEXwC7H9tbtDMdSs9aWil3Ou8hU6z2cOKWbsm/tXPAcsoaHVEtrDo0zcOIE5GF6QgU55tlGL2Nihg==", "dependencies": { - "@lit-labs/ssr-dom-shim": "^1.2.0", - "@lit/reactive-element": "^2.0.4", - "lit-html": "^3.1.2" + "@lit-labs/ssr-dom-shim": "^1.1.2", + "@lit/reactive-element": "^2.0.0", + "lit-html": "^3.1.0" } }, "node_modules/lit-html": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.1.2.tgz", - "integrity": "sha512-3OBZSUrPnAHoKJ9AMjRL/m01YJxQMf+TMHanNtTHG68ubjnZxK0RFl102DPzsw4mWnHibfZIBJm3LWCZ/LmMvg==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.1.0.tgz", + "integrity": "sha512-FwAjq3iNsaO6SOZXEIpeROlJLUlrbyMkn4iuv4f4u1H40Jw8wkeR/OUXZUHUoiYabGk8Y4Y0F/rgq+R4MrOLmA==", "dependencies": { "@types/trusted-types": "^2.0.2" } @@ -23635,9 +22996,9 @@ } }, "node_modules/node-gyp-build": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.0.tgz", - "integrity": "sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.7.0.tgz", + "integrity": "sha512-PbZERfeFdrHQOOXiAKOY0VPbykZy90ndPKk0d+CFDegTKmWp1VgOTz2xACVbr1BjCWxrQp68CXtvNsveFhqDJg==", "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", @@ -24467,9 +23828,9 @@ } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", - "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.3.tgz", + "integrity": "sha512-B7gr+F6MkqB3uzINHXNctGieGsRTMwIBgxkp0yq/5BwcuDzD4A8wQpHQW6vDAm1uKSLQghmRdD9sKqf2vJ1cEg==", "engines": { "node": "14 || >=16.14" } @@ -24786,9 +24147,9 @@ } }, "node_modules/preact": { - "version": "10.19.6", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.19.6.tgz", - "integrity": "sha512-gympg+T2Z1fG1unB8NH29yHJwnEaCH37Z32diPDku316OTnRPeMbiRV9kTrfZpocXjdfnWuFUl/Mj4BHaf6gnw==", + "version": "10.19.2", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.19.2.tgz", + "integrity": "sha512-UA9DX/OJwv6YwP9Vn7Ti/vF80XL+YA5H2l7BpCtUr3ya8LWHFzpiO5R+N7dN16ujpIxhekRFuOOF82bXX7K/lg==", "funding": { "type": "opencollective", "url": "https://opencollective.com/preact" @@ -26287,10 +25648,10 @@ "fsevents": "~2.3.2" } }, - "node_modules/rollup-plugin-visualizer": { - "version": "5.12.0", - "resolved": "https://registry.npmjs.org/rollup-plugin-visualizer/-/rollup-plugin-visualizer-5.12.0.tgz", - "integrity": "sha512-8/NU9jXcHRs7Nnj07PF2o4gjxmm9lXIrZ8r175bT9dK8qoLlvKTwRMArRCMgpMGlq8CTLugRvEmyMeMXIU2pNQ==", + "node_modules/rpc-websockets": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/rpc-websockets/-/rpc-websockets-7.8.0.tgz", + "integrity": "sha512-AStkq6KDvSAmA4WiwlK1pDvj/33BWmExTATUokC0v+NhWekXSTNzXS5OGXeYwq501/pj6lBZMofg/h4dx4/tCg==", "dependencies": { "open": "^8.4.0", "picomatch": "^2.3.1", @@ -27070,9 +26431,9 @@ } }, "node_modules/std-env": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz", - "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==" + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.5.0.tgz", + "integrity": "sha512-JGUEaALvL0Mf6JCfYnJOTcobY+Nc7sG/TemDRBqCA0wEr4DER7zDchaaixTlmOxAjG1uRJmX82EQcxwTQTkqVA==" }, "node_modules/store2": { "version": "2.14.3", @@ -28266,9 +27627,9 @@ "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" }, "node_modules/types-ramda": { - "version": "0.29.9", - "resolved": "https://registry.npmjs.org/types-ramda/-/types-ramda-0.29.9.tgz", - "integrity": "sha512-B+VbLtW68J4ncG/rccKaYDhlirKlVH/Izh2JZUfaPJv+3Tl2jbbgYsB1pvole1vXKSgaPlAe/wgEdOnMdAu52A==", + "version": "0.29.6", + "resolved": "https://registry.npmjs.org/types-ramda/-/types-ramda-0.29.6.tgz", + "integrity": "sha512-VJoOk1uYNh9ZguGd3eZvqkdhD4hTGtnjRBUx5Zc0U9ftmnCgiWcSj/lsahzKunbiwRje1MxxNkEy1UdcXRCpYw==", "dev": true, "dependencies": { "ts-toolbelt": "^9.6.0" @@ -28288,9 +27649,9 @@ } }, "node_modules/ufo": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.4.0.tgz", - "integrity": "sha512-Hhy+BhRBleFjpJ2vchUNN40qgkh0366FWJGqVLYBHev0vpHTrXSA0ryT+74UiW6KWsldNurQMKGqCm1M2zBciQ==" + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.3.2.tgz", + "integrity": "sha512-o+ORpgGwaYQXgqGDwd+hkS4PuZ3QnmqMMxRuajK/a38L6fTpcE5GPIfrf+L/KemFzfUpeUQc1rRS1iDBozvnFA==" }, "node_modules/uglify-js": { "version": "3.17.4", @@ -28463,18 +27824,15 @@ } }, "node_modules/unplugin": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.9.0.tgz", - "integrity": "sha512-14PslvMY3gNbXnQtNIRB566Q057L5Fe7f5LDEamxVi0QQVxoz5hrveBwwZLcKyHtZ09ysmipxRRj5Lv+BGz2Iw==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.5.1.tgz", + "integrity": "sha512-0QkvG13z6RD+1L1FoibQqnvTwVBXvS4XSPwAyinVgoOCl2jAgwzdUKmEj05o4Lt8xwQI85Hb6mSyYkcAGwZPew==", "dev": true, "dependencies": { - "acorn": "^8.11.3", - "chokidar": "^3.6.0", + "acorn": "^8.11.2", + "chokidar": "^3.5.3", "webpack-sources": "^3.2.3", - "webpack-virtual-modules": "^0.6.1" - }, - "engines": { - "node": ">=14.0.0" + "webpack-virtual-modules": "^0.6.0" } }, "node_modules/unstorage": { @@ -28548,9 +27906,9 @@ } }, "node_modules/unstorage/node_modules/lru-cache": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", - "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.3.tgz", + "integrity": "sha512-B7gr+F6MkqB3uzINHXNctGieGsRTMwIBgxkp0yq/5BwcuDzD4A8wQpHQW6vDAm1uKSLQghmRdD9sKqf2vJ1cEg==", "engines": { "node": "14 || >=16.14" } diff --git a/packages/core/src/controllers/ConnectionController.ts b/packages/core/src/controllers/ConnectionController.ts index 3e4b2ed89d..79ba43a858 100644 --- a/packages/core/src/controllers/ConnectionController.ts +++ b/packages/core/src/controllers/ConnectionController.ts @@ -2,7 +2,7 @@ import { subscribeKey as subKey } from 'valtio/utils' import { proxy, ref } from 'valtio/vanilla' import { CoreHelperUtil } from '../utils/CoreHelperUtil.js' import { StorageUtil } from '../utils/StorageUtil.js' -import type { Connector, WcWallet } from '../utils/TypeUtil.js' +import type { Connector, SendTransactionArgs, WcWallet } from '../utils/TypeUtil.js' import { TransactionsController } from './TransactionsController.js' // -- Types --------------------------------------------- // @@ -17,6 +17,9 @@ export interface ConnectionControllerClient { connectWalletConnect: (onUri: (uri: string) => void) => Promise disconnect: () => Promise signMessage: (message: string) => Promise + sendTransaction: (args: SendTransactionArgs) => Promise<{ hash?: string }> + parseUnits: (value: string, decimals: number) => bigint + formatUnits: (value: bigint, decimals: number) => string connectExternal?: (options: ConnectExternalOptions) => Promise checkInstalled?: (ids?: string[]) => boolean } @@ -83,6 +86,18 @@ export const ConnectionController = { return this._getClient().signMessage(message) }, + parseUnits(value: string, decimals: number) { + return this._getClient().parseUnits(value, decimals) + }, + + formatUnits(value: bigint, decimals: number) { + return this._getClient().formatUnits(value, decimals) + }, + + async sendTransaction(args: SendTransactionArgs) { + return this._getClient().sendTransaction(args) + }, + checkInstalled(ids?: string[]) { return this._getClient().checkInstalled?.(ids) }, diff --git a/packages/core/src/controllers/RouterController.ts b/packages/core/src/controllers/RouterController.ts index 6d77df74cb..b6dd5649eb 100644 --- a/packages/core/src/controllers/RouterController.ts +++ b/packages/core/src/controllers/RouterController.ts @@ -45,6 +45,7 @@ export interface RouterControllerState { network?: CaipNetwork email?: string newEmail?: string + target?: 'sourceToken' | 'toToken' } } diff --git a/packages/core/src/controllers/SwapApiController.ts b/packages/core/src/controllers/SwapApiController.ts index 4b953156ff..072ad73ea5 100644 --- a/packages/core/src/controllers/SwapApiController.ts +++ b/packages/core/src/controllers/SwapApiController.ts @@ -1,45 +1,127 @@ import { subscribeKey as subKey } from 'valtio/utils' -import { proxy } from 'valtio/vanilla' +import { proxy, subscribe as sub } from 'valtio/vanilla' import { CoreHelperUtil } from '../utils/CoreHelperUtil.js' import { NetworkController } from './NetworkController.js' import { FetchUtil } from '../utils/FetchUtil.js' import { AccountController } from './AccountController.js' +import { ConstantsUtil } from '../utils/ConstantsUtil.js' +import { ConnectionController } from './ConnectionController.js' // -- Types --------------------------------------------- // export interface SwapApiControllerState { + initialLoading?: boolean + isTransactionPending?: boolean + sourceToken?: TokenInfo sourceTokenAddress?: `0x${string}` + toToken?: TokenInfo toTokenAddress?: `0x${string}` + toTokenAmount?: string + swapTransaction?: TransactionData + swapApproval?: SwapApprovalData sourceTokenAmount?: string slippage?: number disableEstimate?: boolean allowPartialFill?: boolean + hasAllowance: boolean loading?: boolean + tokens?: Record + foundTokens?: TokenInfo[] + myTokensWithBalance?: Record + swapErrorMessage?: string +} + +export interface TokenInfo { + address: `0x${string}` + symbol: string + name: string + decimals: number + logoURI: string + domainVersion?: string + eip2612?: boolean + isFoT?: boolean + tags?: string[] +} + +export interface TokenInfoWithBalance extends TokenInfo { + balance: string + price: string +} + +interface TransactionData { + from: string + to: `0x${string}` + data: `0x${string}` + value: string + gas: number + gasPrice: string +} + +interface SwapResponse { + toAmount: string + tx: TransactionData +} + +interface SwapApprovalData { + data: `0x${string}` + to: `0x${string}` + gasPrice: string + value: string +} + +interface TokenList { + tokens: Record +} + +interface SwapRequestError { + error: string + description: string + statusCode: 400 | 500 + requestId: string + meta: HttpExceptionMeta[] +} + +interface HttpExceptionMeta { + type: string + value: string } type StateKey = keyof SwapApiControllerState // -- State --------------------------------------------- // const state = proxy({ + sourceToken: undefined, sourceTokenAddress: undefined, + toToken: undefined, toTokenAddress: undefined, + hasAllowance: false, + toTokenAmount: undefined, sourceTokenAmount: undefined, slippage: 1, disableEstimate: false, allowPartialFill: false, - loading: false + initialLoading: false, + loading: false, + tokens: undefined, + foundTokens: undefined, + myTokensWithBalance: undefined, + swapErrorMessage: undefined, + isTransactionPending: false }) // -- Controller ---------------------------------------- // export const SwapApiController = { state, + subscribe(callback: (newState: SwapApiControllerState) => void) { + return sub(state, () => callback(state)) + }, + subscribeKey(key: K, callback: (value: SwapApiControllerState[K]) => void) { return subKey(state, key, callback) }, - _getApi() { - const chainId = CoreHelperUtil.getEvmChainId(NetworkController.state.caipNetwork?.id) - const baseUrl = CoreHelperUtil.get1inchApiUrl(chainId) + _get1inchApi() { + const baseUrl = 'https://1inch-swap-proxy.walletconnect-v1-bridge.workers.dev' return new FetchUtil({ baseUrl }) }, @@ -51,24 +133,26 @@ export const SwapApiController = { } return { - fromAddress: address, - sourceTokenAddress: state.sourceTokenAddress, - toTokenAddress: state.toTokenAddress, + fromAddress: address as `0x${string}`, + sourceTokenAddress: state.sourceToken?.address, + toTokenAddress: state.toToken?.address, sourceTokenAmount: state.sourceTokenAmount, - slippage: state.slippage + slippage: state.slippage?.toString() } }, - setSourceTokenAddress(sourceTokenAddress: `0x${string}`) { - state.sourceTokenAddress = sourceTokenAddress + setSourceToken(sourceToken?: TokenInfo) { + state.sourceToken = sourceToken + state.sourceTokenAddress = sourceToken?.address }, setSourceTokenAmount(swapFromAmount: string) { state.sourceTokenAmount = swapFromAmount }, - setToTokenAddress(toTokenAddress: `0x${string}`) { - state.toTokenAddress = toTokenAddress + setToToken(toToken?: TokenInfo) { + state.toToken = toToken + state.toTokenAddress = toToken?.address }, setSlippage(slippage: number) { @@ -79,33 +163,328 @@ export const SwapApiController = { state.loading = isLoading }, - async swap(isFusion?: boolean) { - const api = this._getApi() - const path = `${api.baseUrl}/${isFusion ? 'fusion' : 'swap'}` - const body = this._getSwapParams() + switchTokens() { + const newSourceToken = state.toToken + const newToToken = state.sourceToken + this.setSourceToken(newSourceToken) + this.setToToken(newToToken) + + state.sourceTokenAmount = '' + state.toTokenAmount = '' + }, + + clearFoundTokens() { + state.foundTokens = undefined + }, + + clearTokens() { + state.tokens = undefined + }, + + clearMyTokens() { + state.myTokensWithBalance = undefined + }, + + clearError() { + state.swapErrorMessage = undefined + }, + + async getSwapCalldata() { + const api = this._get1inchApi() + const chainId = CoreHelperUtil.getEvmChainId(NetworkController.state.caipNetwork?.id) + + const path = `${api.baseUrl}/swap/v5.2/${chainId}/swap` + const { fromAddress, slippage, sourceTokenAddress, sourceTokenAmount, toTokenAddress } = + this._getSwapParams() + + if (!sourceTokenAmount || !state.sourceToken?.address || !state.toToken?.address) { + return + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const swapTransactionRes = await api.get({ + path, + params: { + src: sourceTokenAddress, + dst: toTokenAddress, + slippage, + from: fromAddress, + amount: ConnectionController.parseUnits( + sourceTokenAmount, + state.sourceToken?.decimals + ).toString() + } + }) + + if (swapTransactionRes.error) { + state.toTokenAmount = undefined + state.swapTransaction = undefined + + const swapTransaction: SwapRequestError = swapTransactionRes + state.swapErrorMessage = swapTransaction.description + } else { + state.swapErrorMessage = undefined + + const swapTransaction: SwapResponse = swapTransactionRes + state.swapTransaction = swapTransaction.tx + state.toTokenAmount = ConnectionController.formatUnits( + BigInt(swapTransaction.toAmount), + state.toToken.decimals + ) + } + }, + + async getSwapApprovalCalldata() { + const api = this._get1inchApi() + const chainId = CoreHelperUtil.getEvmChainId(NetworkController.state.caipNetwork?.id) + const path = `${api.baseUrl}/swap/v5.2/${chainId}/approve/transaction` + const { sourceTokenAddress, sourceTokenAmount } = this._getSwapParams() - const swapTransactionRes = await api.post({ + if (!sourceTokenAmount || !state.sourceToken?.decimals) { + return + } + const res = await api.get({ path, - body + params: { + tokenAddress: sourceTokenAddress, + amount: ConnectionController.parseUnits( + sourceTokenAmount, + state.sourceToken.decimals + ).toString() + } }) - return swapTransactionRes + state.swapApproval = res }, - async hasTokenAllowance() { - const api = this._getApi() - const path = `${api.baseUrl}/approve/allowance` + async getTokenAllowance() { + const api = this._get1inchApi() + const chainId = CoreHelperUtil.getEvmChainId(NetworkController.state.caipNetwork?.id) + const path = `${api.baseUrl}/swap/v5.2/${chainId}/approve/allowance` const { sourceTokenAddress, fromAddress, sourceTokenAmount } = this._getSwapParams() - const res = await api.post<{ allowance: string }>({ + const res = await api.get<{ allowance: string }>({ path, - body: { sourceTokenAddress, fromAddress } + params: { tokenAddress: sourceTokenAddress, walletAddress: fromAddress } }) - if (res?.allowance && sourceTokenAmount) { - return BigInt(res.allowance) >= BigInt(sourceTokenAmount) + if (res?.allowance && sourceTokenAmount && state.sourceToken?.decimals) { + const hasAllowance = + BigInt(res.allowance) >= + ConnectionController.parseUnits(sourceTokenAmount, state.sourceToken?.decimals) + state.hasAllowance = hasAllowance + + return hasAllowance } + state.hasAllowance = false + return false + }, + + async getTokenList(options?: { forceRefetch?: boolean }) { + if (state.tokens && !options?.forceRefetch) { + return state.tokens + } + state.initialLoading = true + const api = this._get1inchApi() + const chainId = CoreHelperUtil.getEvmChainId(NetworkController.state.caipNetwork?.id) + const path = `${api.baseUrl}/swap/v5.2/${chainId}/tokens` + + const res = await api.get({ + path + }) + + state.tokens = Object.entries(res.tokens) + .sort(([, aTokenInfo], [, bTokenInfo]) => { + if (aTokenInfo.symbol < bTokenInfo.symbol) { + return -1 + } + if (aTokenInfo.symbol > bTokenInfo.symbol) { + return 1 + } + + return 0 + }) + .reduce>((limitedTokens, [tokenAddress, tokenInfo]) => { + if (ConstantsUtil.POPULAR_TOKENS.includes(tokenInfo.symbol)) { + limitedTokens[tokenAddress] = tokenInfo + } + + return limitedTokens + }, {}) + + state.initialLoading = false + + return state.tokens + }, + + async searchTokens(searchTerm: string) { + const api = this._get1inchApi() + const chainId = CoreHelperUtil.getEvmChainId(NetworkController.state.caipNetwork?.id) + const path = `${api.baseUrl}/token/v1.2/${chainId}/search` + + const res = await api.get({ + path, + params: { + query: searchTerm + } + }) + + state.foundTokens = res + }, + + async getMyTokensWithBalance(options?: { forceRefetch?: boolean }) { + if (state.myTokensWithBalance && !options?.forceRefetch) { + return state.myTokensWithBalance + } + + state.initialLoading = true + + const api = this._get1inchApi() + const chainId = CoreHelperUtil.getEvmChainId(NetworkController.state.caipNetwork?.id) + const { fromAddress } = this._getSwapParams() + const balancesPath = `${api.baseUrl}/balance/v1.2/${chainId}/balances/${fromAddress}` + + const balances = await api.get>({ + path: balancesPath + }) + + const nonEmptyBalances = Object.entries(balances).reduce>( + (filteredBalances, [tokenAddress, balance]) => { + if (balance !== '0') { + filteredBalances[tokenAddress] = balance + } + + return filteredBalances + }, + {} + ) + + const tokenAddresses = Object.keys(nonEmptyBalances) + + if (!tokenAddresses?.length) { + state.initialLoading = false + + return undefined + } + + const tokensPath = `${api.baseUrl}/token/v1.2/${chainId}/custom` + const tokensSpotPricePath = `${api.baseUrl}/price/v1.1/${chainId}` + + const [tokens, tokensPrice] = await Promise.all([ + api.get>({ + path: tokensPath, + params: { + addresses: tokenAddresses.join(',') + } + }), + api.post>({ + path: tokensSpotPricePath, + body: { + tokens: tokenAddresses, + currency: 'USD' + }, + headers: { + 'content-type': 'application/json' + } + }) + ]) + + const mergedTokensWithBalances = Object.entries(tokens).reduce< + Record + >((mergedTokens, [tokenAddress, tokenInfo], i) => { + mergedTokens[tokenAddress] = { + ...tokenInfo, + balance: balances[tokenAddress] ?? '0', + price: tokensPrice[tokenAddress] ?? '0' + } + if (i === 0) { + this.setSourceToken(tokenInfo) + } + + return mergedTokens + }, {}) + + state.myTokensWithBalance = mergedTokensWithBalances + + state.initialLoading = false + + return mergedTokensWithBalances + }, + + async getTokenSwapInfo() { + try { + if (state.sourceToken?.address && state.toToken?.address) { + state.loading = true + const hasAllowance = await SwapApiController.getTokenAllowance() + + if (hasAllowance) { + await SwapApiController.getSwapCalldata() + } else { + await SwapApiController.getSwapApprovalCalldata() + } + } + } catch (error) { + if (error instanceof Error) { + state.swapErrorMessage = error.message + } + } finally { + state.loading = false + } + }, + + async swapTokens() { + state.isTransactionPending = true + const chainId = CoreHelperUtil.getEvmChainId(NetworkController.state.caipNetwork?.id) + + const { fromAddress } = this._getSwapParams() + + if (state.swapTransaction) { + try { + await ConnectionController.sendTransaction({ + address: fromAddress, + chainId, + data: state.swapTransaction.data, + to: state.swapTransaction.to, + gas: BigInt(state.swapTransaction.gas), + gasPrice: BigInt(state.swapTransaction.gasPrice), + value: BigInt(state.swapTransaction.value) + }) + + await this.getMyTokensWithBalance({ forceRefetch: true }) + } catch (error: unknown) { + if (error instanceof Error) { + state.swapErrorMessage = error.message + } + } finally { + state.isTransactionPending = false + } + } + }, + + async approveSwapTokens() { + const chainId = CoreHelperUtil.getEvmChainId(NetworkController.state.caipNetwork?.id) + + const { fromAddress } = this._getSwapParams() + + if (state.swapApproval) { + const { data, to, gasPrice, value } = state.swapApproval + + try { + await ConnectionController.sendTransaction({ + address: fromAddress, + chainId, + data, + to, + gasPrice: BigInt(gasPrice), + value: BigInt(value) + }) + state.hasAllowance = true + } catch (error: unknown) { + if (error instanceof Error) { + state.swapErrorMessage = error.message + } + } + } } } diff --git a/packages/core/src/utils/ConstantsUtil.ts b/packages/core/src/utils/ConstantsUtil.ts index 0035451b5b..390b43fe3d 100644 --- a/packages/core/src/utils/ConstantsUtil.ts +++ b/packages/core/src/utils/ConstantsUtil.ts @@ -64,7 +64,27 @@ export const ConstantsUtil = { Base: 'base' }, - WC_COINBASE_ONRAMP_APP_ID: 'bf18c88d-495a-463b-b249-0b9d3656cf5e' + WC_COINBASE_ONRAMP_APP_ID: 'bf18c88d-495a-463b-b249-0b9d3656cf5e', + + POPULAR_TOKENS: [ + 'ETH', + 'UNI', + '1INCH', + 'AAVE', + 'GNO', + 'WBTC', + 'DAI', + 'WETH', + 'USDC', + 'USDT', + 'ARB', + 'BAL', + 'BICO', + 'CRV', + 'ENS', + 'MATIC', + 'OP' + ] } export type CoinbasePaySDKChainNameValues = diff --git a/packages/core/src/utils/TypeUtil.ts b/packages/core/src/utils/TypeUtil.ts index a9fdabeac7..0b9ff3e6c5 100644 --- a/packages/core/src/utils/TypeUtil.ts +++ b/packages/core/src/utils/TypeUtil.ts @@ -393,3 +393,13 @@ export type GetQuoteArgs = { amount: string network: string } + +export interface SendTransactionArgs { + to: `0x${string}` + data: `0x${string}` + value: bigint + gas?: bigint + gasPrice: bigint + chainId: number + address: `0x${string}` +} diff --git a/packages/core/tests/controllers/ConnectionController.test.ts b/packages/core/tests/controllers/ConnectionController.test.ts index 3e77ae86fb..23e14c54a7 100644 --- a/packages/core/tests/controllers/ConnectionController.test.ts +++ b/packages/core/tests/controllers/ConnectionController.test.ts @@ -15,13 +15,19 @@ const client: ConnectionControllerClient = { disconnect: async () => Promise.resolve(), signMessage: async (message: string) => Promise.resolve(message), connectExternal: async _id => Promise.resolve(), - checkInstalled: _id => true + checkInstalled: _id => true, + parseUnits: value => BigInt(value), + formatUnits: value => value.toString(), + sendTransaction: () => Promise.resolve({ hash: '0x' }) } const partialClient: ConnectionControllerClient = { connectWalletConnect: async () => Promise.resolve(), disconnect: async () => Promise.resolve(), - signMessage: async (message: string) => Promise.resolve(message) + signMessage: async (message: string) => Promise.resolve(message), + parseUnits: value => BigInt(value), + formatUnits: value => value.toString(), + sendTransaction: () => Promise.resolve({ hash: '0x' }) } // -- Tests -------------------------------------------------------------------- diff --git a/packages/ethers5/src/client.ts b/packages/ethers5/src/client.ts index 8e7103cd7a..917446659e 100644 --- a/packages/ethers5/src/client.ts +++ b/packages/ethers5/src/client.ts @@ -8,6 +8,7 @@ import type { LibraryOptions, NetworkControllerClient, PublicStateControllerState, + SendTransactionArgs, Token } from '@web3modal/scaffold' import { Web3ModalScaffold } from '@web3modal/scaffold' @@ -261,6 +262,38 @@ export class Web3Modal extends Web3ModalScaffold { }) return signature as `0x${string}` + }, + + parseUnits: (value: string, decimals: number) => + ethers.utils.parseUnits(value, decimals).toBigInt(), + + formatUnits: (value: bigint, decimals: number) => ethers.utils.formatUnits(value, decimals), + + sendTransaction: async ({ + address, + chainId, + data, + gasPrice, + to, + value, + gas + }: SendTransactionArgs) => { + const provider = ProviderController.state.provider + const signer = provider?.getSigner() + + const tx = await signer?.sendTransaction({ + chainId, + data, + from: address, + gasPrice, + value, + to, + gasLimit: gas + }) + + await tx?.wait() + + return { hash: tx?.hash } } } diff --git a/packages/scaffold/src/views/w3m-swap-select-token-view/index.ts b/packages/scaffold/src/views/w3m-swap-select-token-view/index.ts index 09b956d68e..5c6826d2eb 100644 --- a/packages/scaffold/src/views/w3m-swap-select-token-view/index.ts +++ b/packages/scaffold/src/views/w3m-swap-select-token-view/index.ts @@ -1,135 +1,31 @@ import { customElement } from '@web3modal/ui' import { LitElement, html } from 'lit' import styles from './styles.js' - -const yourItems = [ - { - name: 'Ethereum', - symbol: 'ETH', - price: '3,324.34', - value: '0.854' - }, - { - name: 'Avalanche', - symbol: 'AVAX', - price: '2,543.12', - value: '0.723' - }, - { - name: 'Bitcoin', - symbol: 'BTC', - price: '47,892.56', - value: '0.654' - }, - { - name: 'Cardano', - symbol: 'ADA', - price: '2.34', - value: '0.345' - }, - { - name: 'Ripple', - symbol: 'XRP', - price: '1.23', - value: '0.456' - } -] - -const popularItems = [ - { - name: 'Solana', - symbol: 'SOL', - price: '134.56', - value: '0.567' - }, - { - name: 'Polkadot', - symbol: 'DOT', - price: '78.90', - value: '0.678' - }, - { - name: 'Dogecoin', - symbol: 'DOGE', - price: '0.123', - value: '0.789' - }, - { - name: 'Chainlink', - symbol: 'LINK', - price: '23.45', - value: '0.890' - }, - { - name: 'Litecoin', - symbol: 'LTC', - price: '145.67', - value: '0.901' - }, - { - name: 'VeChain', - symbol: 'VET', - price: '0.345', - value: '0.012' - }, - { - name: 'Stellar', - symbol: 'XLM', - price: '0.456', - value: '0.123' - }, - { - name: 'Cosmos', - symbol: 'ATOM', - price: '67.89', - value: '0.234' - }, - { - name: 'Terra', - symbol: 'LUNA', - price: '12.34', - value: '0.345' - }, - { - name: 'Filecoin', - symbol: 'FIL', - price: '234.56', - value: '0.456' - }, - { - name: 'TRON', - symbol: 'TRX', - price: '0.789', - value: '0.567' - }, - { - name: 'Ethereum Classic', - symbol: 'ETC', - price: '45.67', - value: '0.678' - }, - { - name: 'Tezos', - symbol: 'XTZ', - price: '6.78', - value: '0.789' - }, - { - name: 'Monero', - symbol: 'XMR', - price: '123.45', - value: '0.890' - } -] +import { ConnectionController, RouterController, SwapApiController } from '@web3modal/core' +import type { TokenInfo } from '@web3modal/core/src/controllers/SwapApiController.js' +import { state } from 'lit/decorators.js' @customElement('w3m-swap-select-token-view') export class W3mSwapSelectTokenView extends LitElement { public static override styles = styles + + @state() private targetToken = RouterController.state.data?.target + // -- Lifecycle ----------------------------------------- // public constructor() { super() } + private onSelectToken(token: TokenInfo) { + if (this.targetToken === 'sourceToken') { + SwapApiController.setSourceToken(token) + } else { + SwapApiController.setToToken(token) + } + SwapApiController.getTokenSwapInfo() + RouterController.goBack() + } + // -- Render -------------------------------------------- // public override render() { return html` @@ -151,18 +47,25 @@ export class W3mSwapSelectTokenView extends LitElement { private templateListTokens() { return html` - + ${SwapApiController.state.myTokensWithBalance && + html` Your tokens - + `} + - ${yourItems.map( - item => html` + ${SwapApiController.state.myTokensWithBalance && + Object.values(SwapApiController.state.myTokensWithBalance).map( + tokenInfo => html` this.onSelectToken(tokenInfo)} > ` @@ -172,14 +75,14 @@ export class W3mSwapSelectTokenView extends LitElement { Popular tokens - ${popularItems.map( - item => html` + ${SwapApiController.state.tokens && + Object.values(SwapApiController.state.tokens).map( + tokenInfo => html` this.onSelectToken(tokenInfo)} > ` diff --git a/packages/scaffold/src/views/w3m-swap-view/index.ts b/packages/scaffold/src/views/w3m-swap-view/index.ts index f26a45b691..98ad1ac977 100644 --- a/packages/scaffold/src/views/w3m-swap-view/index.ts +++ b/packages/scaffold/src/views/w3m-swap-view/index.ts @@ -2,25 +2,105 @@ import { customElement } from '@web3modal/ui' import { LitElement, html } from 'lit' import { state } from 'lit/decorators.js' import styles from './styles.js' -import { SwapApiController } from '@web3modal/core' -import { EventsController } from '@web3modal/core' -import { RouterController } from '@web3modal/core' - -const tokenFrom = 'ETH' -const tokenTo = '' - +import { + SwapApiController, + EventsController, + RouterController, + CoreHelperUtil, + NetworkController +} from '@web3modal/core' +import type { TokenInfo } from '@web3modal/core/src/controllers/SwapApiController.js' +type Target = 'sourceToken' | 'toToken' @customElement('w3m-swap-view') export class W3mSwapView extends LitElement { public static override styles = styles + private unsubscribe: ((() => void) | undefined)[] = [] + // -- State & Properties -------------------------------- // + @state() private initialLoading = SwapApiController.state.initialLoading + + @state() private isTransactionPending = SwapApiController.state.isTransactionPending + @state() private loading = SwapApiController.state.loading - @state() private networkSrc?: string + @state() private sourceToken = SwapApiController.state.sourceToken + + @state() private toToken = SwapApiController.state.toToken + + @state() private swapErrorMessage = SwapApiController.state.swapErrorMessage + + @state() private hasAllowance = SwapApiController.state.hasAllowance + + @state() private sourceTokenAmount = SwapApiController.state.sourceTokenAmount ?? '' + + @state() private toTokenAmount = SwapApiController.state.toTokenAmount ?? '' + + @state() private caipNetworkId = NetworkController.state.caipNetwork?.id // -- Lifecycle ----------------------------------------- // public constructor() { super() + + NetworkController.subscribeKey('caipNetwork', newCaipNetwork => { + if (this.caipNetworkId !== newCaipNetwork?.id) { + this.caipNetworkId = newCaipNetwork?.id + SwapApiController.setSourceToken(undefined) + SwapApiController.setToToken(undefined) + SwapApiController.clearMyTokens() + SwapApiController.clearTokens() + if (!this.initialLoading) { + SwapApiController.getMyTokensWithBalance({ forceRefetch: true }) + SwapApiController.getTokenList({ forceRefetch: true }) + } + } + }) + + this.unsubscribe.push( + ...[ + SwapApiController.subscribe(newState => { + if (this.loading !== newState.loading) { + this.loading = newState.loading + } + if (this.initialLoading !== newState.initialLoading) { + this.initialLoading = newState.initialLoading + } + this.isTransactionPending = newState.isTransactionPending + if (this.sourceTokenAmount !== newState.sourceTokenAmount) { + this.sourceTokenAmount = newState.sourceTokenAmount ?? '' + } + if (newState.sourceTokenAddress) { + this.sourceToken = newState.sourceToken + } + if (newState.toTokenAddress) { + this.toToken = newState.toToken + } + if (this.toTokenAmount !== newState.toTokenAmount) { + this.toTokenAmount = newState.toTokenAmount ?? '' + } + if (this.sourceToken?.address !== newState.sourceToken?.address) { + this.sourceToken = newState.sourceToken + } + if (this.swapErrorMessage !== newState.swapErrorMessage) { + this.swapErrorMessage = newState.swapErrorMessage + } + if (this.hasAllowance !== newState.hasAllowance) { + this.hasAllowance = newState.hasAllowance + } + }) + ] + ) + } + + public override firstUpdated() { + if (!this.initialLoading) { + SwapApiController.getMyTokensWithBalance() + SwapApiController.getTokenList() + } + } + + public override disconnectedCallback() { + this.unsubscribe.forEach(unsubscribe => unsubscribe?.()) } private getInputElement(el: HTMLElement) { @@ -31,27 +111,51 @@ export class W3mSwapView extends LitElement { return null } - private handleInput(e: InputEvent) { - const inputElement = e.target as HTMLElement + private onInputChange(event: InputEvent) { + const inputElement = event.target as HTMLElement const input = this.getInputElement(inputElement) - + SwapApiController.clearError() if (input) { - const inputValue = input.value - SwapApiController.setSourceTokenAmount(inputValue) + SwapApiController.setSourceTokenAmount(input.value) + this.onDebouncedGetSwapCalldata() } } - private onSwap() { - const amount = SwapApiController.state.sourceTokenAmount - // eslint-disable-next-line no-console - console.log({ amount }) + private onDebouncedGetSwapCalldata = CoreHelperUtil.debounce(async () => { + await SwapApiController.getTokenSwapInfo() + }) + + private async onSwap() { + await SwapApiController.swapTokens() + } + + private async onApprove() { + await SwapApiController.approveSwapTokens() + } + + private onSwitchTokens() { + SwapApiController.switchTokens() + } + + private get actionButtonLabel(): string { + if (!this.toToken || !this.sourceToken) { + return 'Pick tokens' + } + + if (!this.toTokenAmount || !this.sourceTokenAmount) { + return 'Enter Amount' + } + + return this.hasAllowance ? 'Swap' : 'Approve' } // -- Render -------------------------------------------- // public override render() { return html` - ${this.loading ? this.templateLoading() : this.templateSwap()} + ${this.initialLoading || this.isTransactionPending + ? this.templateLoading() + : this.templateSwap()} ` } @@ -67,12 +171,18 @@ export class W3mSwapView extends LitElement { .padding=${['xs', 's', 's', 's'] as const} class="swap-inputs-container" > - ${this.templateTokenInput(tokenFrom)} ${this.templateTokenInput(tokenTo)} - ${this.templateReplaceTokensButton()} + ${this.templateTokenInput('sourceToken', this.sourceToken)} + ${this.templateTokenInput('toToken', this.toToken)} ${this.templateReplaceTokensButton()} - - - Enter amount + + ${this.swapErrorMessage} + + this.onSwap() : () => this.onApprove()} + > + ${this.actionButtonLabel} @@ -81,7 +191,7 @@ export class W3mSwapView extends LitElement { private templateReplaceTokensButton() { return html` -
+
` } - private onSelectToken() { + private onSelectToken(target: Target) { EventsController.sendEvent({ type: 'track', event: 'CLICK_SELECT_TOKEN_TO_SWAP' }) - RouterController.push('SwapSelectToken') + RouterController.push('SwapSelectToken', { + target + }) } } diff --git a/packages/ui/src/composites/wui-input-text/index.ts b/packages/ui/src/composites/wui-input-text/index.ts index 65c14cce8f..665a6a675d 100644 --- a/packages/ui/src/composites/wui-input-text/index.ts +++ b/packages/ui/src/composites/wui-input-text/index.ts @@ -24,6 +24,8 @@ export class WuiInputText extends LitElement { @property() public placeholder = '' + @property() public value = '' + @property() public type: InputType = 'text' @property() public keyHint?: HTMLInputElement['enterKeyHint'] @@ -43,7 +45,6 @@ export class WuiInputText extends LitElement { ?disabled=${this.disabled} placeholder=${this.placeholder} @input=${this.dispatchInputChangeEvent.bind(this)} - value=${ifDefined(this.value)} .value=${this.value || ''} /> ` diff --git a/packages/ui/src/composites/wui-token-list-item/index.ts b/packages/ui/src/composites/wui-token-list-item/index.ts index 7dd4b51009..705c9e8b83 100644 --- a/packages/ui/src/composites/wui-token-list-item/index.ts +++ b/packages/ui/src/composites/wui-token-list-item/index.ts @@ -33,11 +33,16 @@ export class WuiTokenListItem extends LitElement { ${this.name} - ${this.price} + ${this.price && + this.amount && + html`$${(parseFloat(this.price) * parseFloat(this.amount)).toFixed(2)}`} ${this.symbol} - ${this.amount} + ${this.amount && + html`${this.amount}`} diff --git a/packages/ui/src/composites/wui-token-list-item/styles.ts b/packages/ui/src/composites/wui-token-list-item/styles.ts index 06e061ebf0..16d8b36a68 100644 --- a/packages/ui/src/composites/wui-token-list-item/styles.ts +++ b/packages/ui/src/composites/wui-token-list-item/styles.ts @@ -2,6 +2,7 @@ import { css } from 'lit' export default css` :host > wui-flex { + cursor: pointer; column-gap: var(--wui-spacing-s); padding: var(--wui-spacing-s); padding-right: var(--wui-spacing-l); From fedbb874dcb72de1ea1cf444a1906ac7c43ded3a Mon Sep 17 00:00:00 2001 From: Cali93 <32299095+Cali93@users.noreply.github.com> Date: Fri, 24 Nov 2023 13:00:44 +0200 Subject: [PATCH 05/96] fix(multiple-requests): fix multiple requests issue --- packages/core/src/controllers/SwapApiController.ts | 4 ++++ packages/scaffold/src/views/w3m-swap-view/index.ts | 6 +----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/core/src/controllers/SwapApiController.ts b/packages/core/src/controllers/SwapApiController.ts index 072ad73ea5..9847ba3b38 100644 --- a/packages/core/src/controllers/SwapApiController.ts +++ b/packages/core/src/controllers/SwapApiController.ts @@ -334,6 +334,10 @@ export const SwapApiController = { }, async getMyTokensWithBalance(options?: { forceRefetch?: boolean }) { + if (state.initialLoading) { + return state.myTokensWithBalance + } + if (state.myTokensWithBalance && !options?.forceRefetch) { return state.myTokensWithBalance } diff --git a/packages/scaffold/src/views/w3m-swap-view/index.ts b/packages/scaffold/src/views/w3m-swap-view/index.ts index 98ad1ac977..92449d7848 100644 --- a/packages/scaffold/src/views/w3m-swap-view/index.ts +++ b/packages/scaffold/src/views/w3m-swap-view/index.ts @@ -49,10 +49,7 @@ export class W3mSwapView extends LitElement { SwapApiController.setToToken(undefined) SwapApiController.clearMyTokens() SwapApiController.clearTokens() - if (!this.initialLoading) { - SwapApiController.getMyTokensWithBalance({ forceRefetch: true }) - SwapApiController.getTokenList({ forceRefetch: true }) - } + SwapApiController.getTokenList({ forceRefetch: true }) } }) @@ -94,7 +91,6 @@ export class W3mSwapView extends LitElement { public override firstUpdated() { if (!this.initialLoading) { - SwapApiController.getMyTokensWithBalance() SwapApiController.getTokenList() } } From 9cf5b70dc4020271a209bc7607936f89d4f7065c Mon Sep 17 00:00:00 2001 From: Enes Date: Fri, 24 Nov 2023 17:07:34 +0300 Subject: [PATCH 06/96] feat: implement remaining pages and ui components (#1493) --- package-lock.json | 8 +- .../core/src/controllers/RouterController.ts | 2 + .../core/src/controllers/SwapApiController.ts | 1 + packages/core/src/utils/TypeUtil.ts | 4 + packages/scaffold/index.ts | 3 + .../scaffold/src/modal/w3m-router/index.ts | 4 + .../scaffold/src/partials/w3m-header/index.ts | 4 +- .../src/views/w3m-preview-swap-view/index.ts | 112 +++++++++++++ .../src/views/w3m-preview-swap-view/styles.ts | 51 ++++++ .../w3m-swap-select-network-view/index.ts | 100 ++++++++++++ .../w3m-swap-select-network-view/styles.ts | 82 ++++++++++ .../views/w3m-swap-select-token-view/index.ts | 22 ++- .../w3m-swap-select-token-view/styles.ts | 31 ++++ .../scaffold/src/views/w3m-swap-view/index.ts | 147 +++++++++++------- .../src/views/w3m-swap-view/styles.ts | 72 +++------ packages/ui/index.ts | 1 + packages/ui/src/assets/svg/checkmark-bold.ts | 10 ++ packages/ui/src/components/wui-icon/index.ts | 2 + packages/ui/src/components/wui-text/styles.ts | 1 + .../ui/src/composites/wui-swap-input/index.ts | 94 +++++++++++ .../src/composites/wui-swap-input/styles.ts | 101 ++++++++++++ .../composites/wui-token-list-item/styles.ts | 9 +- packages/ui/src/utils/TypeUtil.ts | 1 + 23 files changed, 746 insertions(+), 116 deletions(-) create mode 100644 packages/scaffold/src/views/w3m-preview-swap-view/index.ts create mode 100644 packages/scaffold/src/views/w3m-preview-swap-view/styles.ts create mode 100644 packages/scaffold/src/views/w3m-swap-select-network-view/index.ts create mode 100644 packages/scaffold/src/views/w3m-swap-select-network-view/styles.ts create mode 100644 packages/ui/src/assets/svg/checkmark-bold.ts create mode 100644 packages/ui/src/composites/wui-swap-input/index.ts create mode 100644 packages/ui/src/composites/wui-swap-input/styles.ts diff --git a/package-lock.json b/package-lock.json index 49d8185ddb..f27c4d7c41 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27699,14 +27699,14 @@ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, "node_modules/unenv": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/unenv/-/unenv-1.9.0.tgz", - "integrity": "sha512-QKnFNznRxmbOF1hDgzpqrlIf6NC5sbZ2OJ+5Wl3OX8uM+LUJXbj4TXvLJCtwbPTmbMHCLIz6JLKNinNsMShK9g==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/unenv/-/unenv-1.8.0.tgz", + "integrity": "sha512-uIGbdCWZfhRRmyKj1UioCepQ0jpq638j/Cf0xFTn4zD1nGJ2lSdzYHLzfdXN791oo/0juUiSWW1fBklXMTsuqg==", "dependencies": { "consola": "^3.2.3", "defu": "^6.1.3", "mime": "^3.0.0", - "node-fetch-native": "^1.6.1", + "node-fetch-native": "^1.4.1", "pathe": "^1.1.1" } }, diff --git a/packages/core/src/controllers/RouterController.ts b/packages/core/src/controllers/RouterController.ts index b6dd5649eb..9ff4c57de5 100644 --- a/packages/core/src/controllers/RouterController.ts +++ b/packages/core/src/controllers/RouterController.ts @@ -38,6 +38,8 @@ export interface RouterControllerState { | 'WhatIsABuy' | 'Swap' | 'SwapSelectToken' + | 'SwapSelectNetwork' + | 'PreviewSwap' history: RouterControllerState['view'][] data?: { connector?: Connector diff --git a/packages/core/src/controllers/SwapApiController.ts b/packages/core/src/controllers/SwapApiController.ts index 9847ba3b38..786d52869f 100644 --- a/packages/core/src/controllers/SwapApiController.ts +++ b/packages/core/src/controllers/SwapApiController.ts @@ -166,6 +166,7 @@ export const SwapApiController = { switchTokens() { const newSourceToken = state.toToken const newToToken = state.sourceToken + this.setSourceToken(newSourceToken) this.setToToken(newToToken) diff --git a/packages/core/src/utils/TypeUtil.ts b/packages/core/src/utils/TypeUtil.ts index 0b9ff3e6c5..b97d903696 100644 --- a/packages/core/src/utils/TypeUtil.ts +++ b/packages/core/src/utils/TypeUtil.ts @@ -332,6 +332,10 @@ export type Event = type: 'track' event: 'CLICK_SELECT_TOKEN_TO_SWAP' } + | { + type: 'track' + event: 'CLICK_SELECT_NETWORK_TO_SWAP' + } // Onramp Types export type DestinationWallet = { diff --git a/packages/scaffold/index.ts b/packages/scaffold/index.ts index 6a54f1f4d2..f8d8d3c75d 100644 --- a/packages/scaffold/index.ts +++ b/packages/scaffold/index.ts @@ -22,7 +22,10 @@ export * from './src/views/w3m-onramp-fiat-select-view/index.js' export * from './src/views/w3m-onramp-providers-view/index.js' export * from './src/views/w3m-onramp-tokens-select-view/index.js' export * from './src/views/w3m-swap-view/index.js' +export * from './src/views/w3m-preview-swap-view/index.js' +export * from './src/views/w3m-swap-select-network-view/index.js' export * from './src/views/w3m-swap-select-token-view/index.js' +export * from './src/views/w3m-swap-view/index.js' export * from './src/views/w3m-transactions-view/index.js' export * from './src/views/w3m-what-is-a-network-view/index.js' export * from './src/views/w3m-what-is-a-wallet-view/index.js' diff --git a/packages/scaffold/src/modal/w3m-router/index.ts b/packages/scaffold/src/modal/w3m-router/index.ts index 7c1a039b87..ae83708556 100644 --- a/packages/scaffold/src/modal/w3m-router/index.ts +++ b/packages/scaffold/src/modal/w3m-router/index.ts @@ -121,6 +121,10 @@ export class W3mRouter extends LitElement { return html`` case 'SwapSelectToken': return html`` + case 'SwapSelectNetwork': + return html`` + case 'PreviewSwap': + return html`` default: return html`` } diff --git a/packages/scaffold/src/partials/w3m-header/index.ts b/packages/scaffold/src/partials/w3m-header/index.ts index ca1294cb16..d8751813dc 100644 --- a/packages/scaffold/src/partials/w3m-header/index.ts +++ b/packages/scaffold/src/partials/w3m-header/index.ts @@ -54,7 +54,9 @@ function headings() { WalletReceive: 'Receive', WalletCompatibleNetworks: 'Compatible Networks', Swap: 'Swap', - SwapSelectToken: 'Select Token' + SwapSelectToken: 'Select Token', + SwapSelectNetwork: 'Select Network', + PreviewSwap: 'Preview Swap' } } diff --git a/packages/scaffold/src/views/w3m-preview-swap-view/index.ts b/packages/scaffold/src/views/w3m-preview-swap-view/index.ts new file mode 100644 index 0000000000..f52c723391 --- /dev/null +++ b/packages/scaffold/src/views/w3m-preview-swap-view/index.ts @@ -0,0 +1,112 @@ +import { customElement } from '@web3modal/ui' +import { LitElement, html } from 'lit' +import styles from './styles.js' + +@customElement('w3m-preview-swap-view') +export class W3mPreviewSwapView extends LitElement { + public static override styles = styles + + // -- Lifecycle ----------------------------------------- // + public constructor() { + super() + } + + // -- Render -------------------------------------------- // + public override render() { + return html` + ${this.templateSwap()} + ` + } + + // -- Private ------------------------------------------- // + private templateSwap() { + return html` + + + ${this.templateToken('0.867')} + + + + ${this.templateToken('4927.3664')} + $1,718.6 + + + + + Price + + 1 ETH = 5,700.05 1INCH + $2,003.62 + + + + + Network + + Ethereum + + + + + + Network cost + + 15.4007 1INCH + $5.3836 + + + + + Service fee + + Free + + + + + + + + ` + } + + private templateToken(value: string, networkSrc?: string) { + const networkElement = networkSrc + ? html`` + : html` + + ` + + return html` + + ${value} + ${networkElement} + + ` + } +} + +declare global { + interface HTMLElementTagNameMap { + 'w3m-preview-swap-view': W3mPreviewSwapView + } +} diff --git a/packages/scaffold/src/views/w3m-preview-swap-view/styles.ts b/packages/scaffold/src/views/w3m-preview-swap-view/styles.ts new file mode 100644 index 0000000000..30fa42fa01 --- /dev/null +++ b/packages/scaffold/src/views/w3m-preview-swap-view/styles.ts @@ -0,0 +1,51 @@ +import { css } from 'lit' + +export default css` + :host > wui-flex:first-child { + overflow-y: auto; + overflow-x: hidden; + scrollbar-width: none; + } + + .preview-container, + .details-container { + width: 100%; + } + + .token-image { + width: 24px; + height: 24px; + box-shadow: 0 0 0 2px var(--wui-gray-glass-005); + border-radius: 12px; + } + + wui-loading-hexagon { + position: absolute; + } + + .token-item { + display: flex; + align-items: center; + justify-content: center; + gap: var(--wui-spacing-xxs); + padding: var(--wui-spacing-xs); + height: 40px; + border: none; + border-radius: 80px; + background: var(--wui-gray-glass-002); + box-shadow: inset 0 0 0 1px var(--wui-gray-glass-002); + cursor: pointer; + transition: background 0.2s linear; + } + + .token-item:hover { + background: var(--wui-gray-glass-005); + } + + .details-row { + width: 100%; + padding: var(--wui-spacing-s) var(--wui-spacing-xl); + border-radius: var(--wui-border-radius-xxs); + background: var(--wui-gray-glass-002); + } +` diff --git a/packages/scaffold/src/views/w3m-swap-select-network-view/index.ts b/packages/scaffold/src/views/w3m-swap-select-network-view/index.ts new file mode 100644 index 0000000000..e2cf9757af --- /dev/null +++ b/packages/scaffold/src/views/w3m-swap-select-network-view/index.ts @@ -0,0 +1,100 @@ +import { customElement } from '@web3modal/ui' +import { LitElement, html } from 'lit' +import styles from './styles.js' +import { state } from 'lit/decorators.js' +import { AccountController, NetworkController } from '@web3modal/core' + +const yourItems = [ + { + name: 'Ethereum', + symbol: 'ETH' + }, + { + name: 'Avalanche', + symbol: 'AVAX' + }, + { + name: 'Bitcoin', + symbol: 'BTC' + }, + { + name: 'Cardano', + symbol: 'ADA' + }, + { + name: 'Ripple', + symbol: 'XRP' + } +] + +@customElement('w3m-swap-select-network-view') +export class W3mSwapSelectNetworkView extends LitElement { + public static override styles = styles + // -- Members ------------------------------------------- // + private unsubscribe: (() => void)[] = [] + + // -- State & Properties -------------------------------- // + @state() private network = NetworkController.state.caipNetwork + + @state() private connected = AccountController.state.isConnected + + // -- Lifecycle ----------------------------------------- // + public constructor() { + super() + this.unsubscribe.push( + ...[ + NetworkController.subscribeKey('caipNetwork', val => (this.network = val)), + AccountController.subscribeKey('isConnected', val => (this.connected = val)) + ] + ) + } + + public override disconnectedCallback() { + this.unsubscribe.forEach(unsubscribe => unsubscribe()) + } + + // -- Render -------------------------------------------- // + public override render() { + console.log('network', this.network) + return html` + ${this.templateListNetworks()} + ` + } + + // -- Private ------------------------------------------- // + private templateListNetworks() { + return html` + + + ${yourItems.map( + item => html`${this.templateNetworkListItem(item, this.network?.name === item.name)}` + )} + + + ` + } + + private templateNetworkListItem(item: { name: string; symbol: string }, active?: boolean) { + return html`` + } +} + +declare global { + interface HTMLElementTagNameMap { + 'w3m-swap-select-network-view': W3mSwapSelectNetworkView + } +} diff --git a/packages/scaffold/src/views/w3m-swap-select-network-view/styles.ts b/packages/scaffold/src/views/w3m-swap-select-network-view/styles.ts new file mode 100644 index 0000000000..1ec529bdf0 --- /dev/null +++ b/packages/scaffold/src/views/w3m-swap-select-network-view/styles.ts @@ -0,0 +1,82 @@ +import { css } from 'lit' + +export default css` + :host > wui-flex:first-child { + overflow-y: hidden; + overflow-x: hidden; + scrollbar-width: none; + } + + wui-loading-hexagon { + position: absolute; + } + + .network-list-item { + background: none; + border: none; + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--wui-spacing-s); + padding: var(--wui-spacing-s); + border-radius: var(--wui-border-radius-xs); + outline: none; + } + + .network-list-item:hover { + background: var(--wui-gray-glass-001); + } + + .network-list-item:focus-visible { + box-shadow: inset 0 0 0 2px var(--wui-gray-glass-010); + } + + .network-list-item.active { + background: var(--wui-gray-glass-002); + } + + .network-list-item wui-image { + width: 40px; + height: 40px; + border-radius: var(--wui-border-radius-s); + box-shadow: inset 0 0 0 2px var(--wui-gray-glass-010); + } + + .network-list-item wui-icon { + width: 14px; + height: 14px; + } + + .token-list { + padding-top: var(--wui-spacing-s); + max-height: calc(512px); + overflow-y: auto; + border-top-left-radius: 30px; + border-top-right-radius: 30px; + } + + .network-search-input, + .select-network-button { + height: 40px; + } + + .select-network-button { + box-shadow: inset 0 0 0 1px var(--wui-gray-glass-005); + background-color: var(--wui-gray-glass-002); + border-radius: var(--wui-border-radius-xxs); + padding: var(--wui-spacing-xs); + align-items: center; + transition: background-color 0.2s linear; + } + + .select-network-button:hover { + background-color: var(--wui-gray-glass-005); + } + + .select-network-button > wui-image { + width: 26px; + height: 26px; + border-radius: var(--wui-border-radius-xs); + box-shadow: inset 0 0 0 1px var(--wui-gray-glass-010); + } +` diff --git a/packages/scaffold/src/views/w3m-swap-select-token-view/index.ts b/packages/scaffold/src/views/w3m-swap-select-token-view/index.ts index 5c6826d2eb..6e0e640669 100644 --- a/packages/scaffold/src/views/w3m-swap-select-token-view/index.ts +++ b/packages/scaffold/src/views/w3m-swap-select-token-view/index.ts @@ -5,6 +5,8 @@ import { ConnectionController, RouterController, SwapApiController } from '@web3 import type { TokenInfo } from '@web3modal/core/src/controllers/SwapApiController.js' import { state } from 'lit/decorators.js' +import { EventsController } from '@web3modal/core' + @customElement('w3m-swap-select-token-view') export class W3mSwapSelectTokenView extends LitElement { public static override styles = styles @@ -39,7 +41,18 @@ export class W3mSwapSelectTokenView extends LitElement { private templateSearchInput() { return html` - + + ` } @@ -52,7 +65,7 @@ export class W3mSwapSelectTokenView extends LitElement { Your tokens `} - + ${SwapApiController.state.myTokensWithBalance && Object.values(SwapApiController.state.myTokensWithBalance).map( tokenInfo => html` @@ -91,6 +104,11 @@ export class W3mSwapSelectTokenView extends LitElement { ` } + + private onSelectNetwork() { + EventsController.sendEvent({ type: 'track', event: 'CLICK_SELECT_NETWORK_TO_SWAP' }) + RouterController.push('SwapSelectNetwork') + } } declare global { diff --git a/packages/scaffold/src/views/w3m-swap-select-token-view/styles.ts b/packages/scaffold/src/views/w3m-swap-select-token-view/styles.ts index ff47924c2e..d690ca7a98 100644 --- a/packages/scaffold/src/views/w3m-swap-select-token-view/styles.ts +++ b/packages/scaffold/src/views/w3m-swap-select-token-view/styles.ts @@ -34,4 +34,35 @@ export default css` border-top-left-radius: 30px; border-top-right-radius: 30px; } + + .network-search-input, + .select-network-button { + height: 40px; + } + + .select-network-button { + border: none; + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + gap: var(--wui-spacing-xs); + box-shadow: inset 0 0 0 1px var(--wui-gray-glass-005); + background-color: transparent; + border-radius: var(--wui-border-radius-xxs); + padding: var(--wui-spacing-xs); + align-items: center; + transition: background-color 0.2s linear; + } + + .select-network-button:hover { + background-color: var(--wui-gray-glass-002); + } + + .select-network-button > wui-image { + width: 26px; + height: 26px; + border-radius: var(--wui-border-radius-xs); + box-shadow: inset 0 0 0 1px var(--wui-gray-glass-010); + } ` diff --git a/packages/scaffold/src/views/w3m-swap-view/index.ts b/packages/scaffold/src/views/w3m-swap-view/index.ts index 92449d7848..20024b374e 100644 --- a/packages/scaffold/src/views/w3m-swap-view/index.ts +++ b/packages/scaffold/src/views/w3m-swap-view/index.ts @@ -10,7 +10,9 @@ import { NetworkController } from '@web3modal/core' import type { TokenInfo } from '@web3modal/core/src/controllers/SwapApiController.js' + type Target = 'sourceToken' | 'toToken' + @customElement('w3m-swap-view') export class W3mSwapView extends LitElement { public static override styles = styles @@ -38,6 +40,8 @@ export class W3mSwapView extends LitElement { @state() private caipNetworkId = NetworkController.state.caipNetwork?.id + @state() private detailsOpen = false + // -- Lifecycle ----------------------------------------- // public constructor() { super() @@ -110,7 +114,9 @@ export class W3mSwapView extends LitElement { private onInputChange(event: InputEvent) { const inputElement = event.target as HTMLElement const input = this.getInputElement(inputElement) + SwapApiController.clearError() + if (input) { SwapApiController.setSourceTokenAmount(input.value) this.onDebouncedGetSwapCalldata() @@ -119,7 +125,7 @@ export class W3mSwapView extends LitElement { private onDebouncedGetSwapCalldata = CoreHelperUtil.debounce(async () => { await SwapApiController.getTokenSwapInfo() - }) + }, 2000) private async onSwap() { await SwapApiController.swapTokens() @@ -158,28 +164,29 @@ export class W3mSwapView extends LitElement { // -- Private ------------------------------------------- // private templateSwap() { + const haveNoTokenSelected = !this.toToken || !this.sourceToken + return html` - - + + ${this.templateTokenInput('sourceToken', this.sourceToken)} ${this.templateTokenInput('toToken', this.toToken)} ${this.templateReplaceTokensButton()} - - ${this.swapErrorMessage} - - this.onSwap() : () => this.onApprove()} - > - ${this.actionButtonLabel} - + + ${this.templateDetails()} + + + ${haveNoTokenSelected + ? html` + Select token + ` + : html` + Preview swap + `} ` @@ -235,57 +242,83 @@ export class W3mSwapView extends LitElement { ` } - private templateTokenInput(target: Target, token?: TokenInfo) { - return html` - - - - ${this.templateTokenSelectButton(target, token)} - ` + private templateTokenInput(target: Target, token: TokenInfo | undefined) { + return html`` } - private templateTokenSelectButton(target: Target, token?: TokenInfo) { - if (!token) { - return html` this.onSelectToken(target)} - > - Select token - ` - } - - const tokenElement = token.logoURI - ? html`` - : html` - - ` - + private templateDetails() { return html` -
this.onSelectToken(target)}> - -
+ ${this.detailsOpen + ? html` + + + Network cost + + $5.3836 + 15.4007 1INCH + + + + + + Service fee + + Free + + + + + + + Fee is paid to Ethereum Network to process your transaction. This must be paid + in ETH. Learn more + + + + ` + : null} +
` } + private onPreviewSwap() { + RouterController.push('PreviewSwap') + } + private onSelectToken(target: Target) { EventsController.sendEvent({ type: 'track', event: 'CLICK_SELECT_TOKEN_TO_SWAP' }) RouterController.push('SwapSelectToken', { target }) } + + private onFromInputFocus(state: boolean) { + this.fromInputFocus = state + } + + private onToInputFocus(state: boolean) { + this.toInputFocus = state + } + + private toggleDetails() { + this.detailsOpen = !this.detailsOpen + } } declare global { diff --git a/packages/scaffold/src/views/w3m-swap-view/styles.ts b/packages/scaffold/src/views/w3m-swap-view/styles.ts index 58cf60eacd..80fab1ebee 100644 --- a/packages/scaffold/src/views/w3m-swap-view/styles.ts +++ b/packages/scaffold/src/views/w3m-swap-view/styles.ts @@ -43,12 +43,12 @@ export default css` display: flex; justify-content: center; align-items: center; - gap: var(--4XS, 10px);w - width: 40px; + gap: var(--wui-spacing-1xs); height: 40px; - padding: var(--5XS, 8px); + width: 40px; + padding: var(--wui-spacing-xs); border: none; - border-radius: var(--3XS, 12px); + border-radius: var(--wui-border-radius-xxs); background: var(--wui-gray-glass-005); } @@ -57,67 +57,39 @@ export default css` cursor: pointer; } - .swap-input { - display: flex; - flex-direction: row; - justify-content: space-between; - align-items: center; + .details-container > wui-flex { background: var(--wui-gray-glass-002); - border: 1px solid var(--wui-gray-glass-002); - border-radius: var(--wui-border-radius-s); - padding: var(--wui-spacing-xl); - padding-right: var(--wui-spacing-s); + border-radius: var(--wui-border-radius-xxs); width: 100%; - box-sizing: border-box; - position: relative; } - .swap-input input { - background: none; + .details-container > wui-flex > button { border: none; - height: 48px; - width: 100%; - font-size: 32px; - font-style: normal; - font-weight: 400; - line-height: 130%; - letter-spacing: -1.28px; - outline: none; - } - - .swap-input input:focus-visible { - outline: none; + background: none; + padding: var(--wui-spacing-s); + border-radius: var(--wui-border-radius-xxs); + transition: background 0.2s linear; } - .swap-input input::-webkit-outer-spin-button, - .swap-input input::-webkit-inner-spin-button { - -webkit-appearance: none; - margin: 0; + .details-container > wui-flex > button:hover { + background: var(--wui-gray-glass-002); } - .token-select-button-container { + .details-content-container { + padding: var(--wui-spacing-1xs); display: flex; - flex-direction: column; align-items: center; justify-content: center; } - .token-select-button-container button { - display: flex; - align-items: center; - justify-content: center; - gap: var(--wui-spacing-xxs); - padding: var(--wui-spacing-xs); - height: 40px; - border: none; - border-radius: 80px; - background: var(--wui-gray-glass-002); - box-shadow: inset 0 0 0 1px var(--wui-gray-glass-002); - cursor: pointer; - transition: background 0.2s linear; + .details-content-container > wui-flex { + width: 100%; } - .token-select-button-container button:hover { - background: var(--wui-gray-glass-005); + .details-row { + width: 100%; + padding: var(--wui-spacing-s) var(--wui-spacing-xl); + border-radius: var(--wui-border-radius-xxs); + background: var(--wui-gray-glass-002); } ` diff --git a/packages/ui/index.ts b/packages/ui/index.ts index 3e9e314408..5baea5847b 100644 --- a/packages/ui/index.ts +++ b/packages/ui/index.ts @@ -36,6 +36,7 @@ export * from './src/composites/wui-otp/index.js' export * from './src/composites/wui-qr-code/index.js' export * from './src/composites/wui-search-bar/index.js' export * from './src/composites/wui-snackbar/index.js' +export * from './src/composites/wui-swap-input/index.js' export * from './src/composites/wui-tabs/index.js' export * from './src/composites/wui-tag/index.js' export * from './src/composites/wui-tooltip/index.js' diff --git a/packages/ui/src/assets/svg/checkmark-bold.ts b/packages/ui/src/assets/svg/checkmark-bold.ts new file mode 100644 index 0000000000..b7f80f79d4 --- /dev/null +++ b/packages/ui/src/assets/svg/checkmark-bold.ts @@ -0,0 +1,10 @@ +import { svg } from 'lit' + +export const checkmarkBoldSvg = svg` + +` diff --git a/packages/ui/src/components/wui-icon/index.ts b/packages/ui/src/components/wui-icon/index.ts index 93678a1eb3..4b1e4d8e6e 100644 --- a/packages/ui/src/components/wui-icon/index.ts +++ b/packages/ui/src/components/wui-icon/index.ts @@ -71,6 +71,7 @@ import { bankSvg } from '../../assets/svg/bank.js' import { cardSvg } from '../../assets/svg/card.js' import { plusSvg } from '../../assets/svg/plus.js' import { cursorTransparentSvg } from '../../assets/svg/cursor-transparent.js' +import { checkmarkBoldSvg } from '../../assets/svg/checkmark-bold.js' const svgOptions: Record> = { add: addSvg, @@ -86,6 +87,7 @@ const svgOptions: Record> = { browser: browserSvg, card: cardSvg, checkmark: checkmarkSvg, + checkmarkBold: checkmarkBoldSvg, chevronBottom: chevronBottomSvg, chevronLeft: chevronLeftSvg, chevronRight: chevronRightSvg, diff --git a/packages/ui/src/components/wui-text/styles.ts b/packages/ui/src/components/wui-text/styles.ts index 73081751bb..9a8ab6a2b5 100644 --- a/packages/ui/src/components/wui-text/styles.ts +++ b/packages/ui/src/components/wui-text/styles.ts @@ -52,6 +52,7 @@ export default css` letter-spacing: var(--wui-letter-spacing-2xl); } + .wui-font-paragraph-400, .wui-font-paragraph-500, .wui-font-paragraph-600, .wui-font-paragraph-700 { diff --git a/packages/ui/src/composites/wui-swap-input/index.ts b/packages/ui/src/composites/wui-swap-input/index.ts new file mode 100644 index 0000000000..e951ed9657 --- /dev/null +++ b/packages/ui/src/composites/wui-swap-input/index.ts @@ -0,0 +1,94 @@ +import { html, LitElement } from 'lit' +import { property } from 'lit/decorators.js' +import { customElement } from '../../utils/WebComponentsUtil.js' +import { resetStyles } from '../../utils/ThemeUtil.js' +import '../../components/wui-text/index.js' +import '../wui-transaction-visual/index.js' +import { EventsController, RouterController } from '@web3modal/core' +import styles from './styles.js' +import type { TokenInfo } from '@web3modal/core/dist/types/src/controllers/SwapApiController.js' + +type Target = 'sourceToken' | 'toToken' + +@customElement('wui-swap-input') +export class WuiSwapInput extends LitElement { + public static override styles = [resetStyles, styles] + + // -- State & Properties -------------------------------- // + @property() public focused: boolean = false + + @property() public value?: string + + @property() public disabled?: boolean + + @property() public target: Target = 'sourceToken' + + @property() public token: TokenInfo | undefined + + @property() public onChange?: (event: InputEvent) => void + + // -- Render -------------------------------------------- // + public override render() { + console.log('swap-input', this.token, this.target) + + return html` + + + this.onFocusChange(true)} + @focusout=${() => this.onFocusChange(false)} + .value=${this.value} + ?disabled=${this.disabled} + /> + + ${this.templateTokenSelectButton()} + + ` + } + + // -- Private ------------------------------------------- // + private templateTokenSelectButton() { + if (!this.token) { + return html` + Select token + ` + } + + const tokenElement = this.token.logoURI + ? html`` + : html` + + ` + + return html` +
+ +
+ ` + } + + private onFocusChange(state: boolean) { + this.focused = state + } + + private onSelectToken(target: Target) { + EventsController.sendEvent({ type: 'track', event: 'CLICK_SELECT_TOKEN_TO_SWAP' }) + RouterController.push('SwapSelectToken', { + target: this.target + }) + } +} + +declare global { + interface HTMLElementTagNameMap { + 'wui-swap-input': WuiSwapInput + } +} diff --git a/packages/ui/src/composites/wui-swap-input/styles.ts b/packages/ui/src/composites/wui-swap-input/styles.ts new file mode 100644 index 0000000000..0ad6511475 --- /dev/null +++ b/packages/ui/src/composites/wui-swap-input/styles.ts @@ -0,0 +1,101 @@ +import { css } from 'lit' + +export default css` + :host > wui-flex { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + background: var(--wui-gray-glass-002); + border: 1px solid var(--wui-gray-glass-002); + border-radius: var(--wui-border-radius-s); + padding: var(--wui-spacing-xl); + padding-right: var(--wui-spacing-s); + width: 100%; + box-sizing: border-box; + position: relative; + transition: background 0.2s linear; + } + + :host > wui-flex.focus { + background: var(--wui-gray-glass-005); + } + + :host > wui-flex .swap-input { + -webkit-mask-image: linear-gradient( + 270deg, + transparent 0px, + transparent 8px, + black 24px, + black 25px, + black 32px, + black 100% + ); + mask-image: linear-gradient( + 270deg, + transparent 0px, + transparent 8px, + black 24px, + black 25px, + black 32px, + black 100% + ); + } + + :host > wui-flex .swap-input input { + background: none; + border: none; + height: 48px; + width: 100%; + font-size: 32px; + font-style: normal; + font-weight: 400; + line-height: 130%; + letter-spacing: -1.28px; + outline: none; + } + + :host > wui-flex .swap-input input:focus-visible { + outline: none; + } + + :host > wui-flex .swap-input input::-webkit-outer-spin-button, + :host > wui-flex .swap-input input::-webkit-inner-spin-button { + -webkit-appearance: none; + margin: 0; + } + + .token-select-button-container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + } + + .token-select-button-container button { + display: flex; + align-items: center; + justify-content: center; + gap: var(--wui-spacing-xxs); + padding: var(--wui-spacing-xs); + padding-right: var(--wui-spacing-1xs); + height: 40px; + border: none; + border-radius: 80px; + background: var(--wui-gray-glass-002); + box-shadow: inset 0 0 0 1px var(--wui-gray-glass-002); + cursor: pointer; + transition: background 0.2s linear; + } + + .token-select-button-container button:hover { + background: var(--wui-gray-glass-005); + } + + .token-select-button wui-image { + width: 24px; + height: 24px; + border-radius: var(--wui-border-radius-s); + box-shadow: inset 0 0 0 1px var(--wui-gray-glass-010); + } +` diff --git a/packages/ui/src/composites/wui-token-list-item/styles.ts b/packages/ui/src/composites/wui-token-list-item/styles.ts index 16d8b36a68..c1530c5b66 100644 --- a/packages/ui/src/composites/wui-token-list-item/styles.ts +++ b/packages/ui/src/composites/wui-token-list-item/styles.ts @@ -4,12 +4,17 @@ export default css` :host > wui-flex { cursor: pointer; column-gap: var(--wui-spacing-s); - padding: var(--wui-spacing-s); + padding: var(--wui-spacing-xs); padding-right: var(--wui-spacing-l); width: 100%; - background-color: var(--wui-gray-glass-002); + background-color: transparent; border-radius: var(--wui-border-radius-xs); color: var(--wui-color-fg-250); + transition: background-color 0.2s linear; + } + + :host > wui-flex:hover { + background-color: var(--wui-gray-glass-002); } :host > wui-flex > wui-flex { diff --git a/packages/ui/src/utils/TypeUtil.ts b/packages/ui/src/utils/TypeUtil.ts index 83989646bc..af7e1b72b2 100644 --- a/packages/ui/src/utils/TypeUtil.ts +++ b/packages/ui/src/utils/TypeUtil.ts @@ -106,6 +106,7 @@ export type IconType = | 'browser' | 'card' | 'checkmark' + | 'checkmarkBold' | 'chevronBottom' | 'chevronLeft' | 'chevronRight' From d5ee070dfcd5a7fd76b2dfc4ddd503738b2858cf Mon Sep 17 00:00:00 2001 From: enesozturk Date: Fri, 24 Nov 2023 17:45:31 +0300 Subject: [PATCH 07/96] refactor: update swap token state logics --- .../core/src/controllers/SwapApiController.ts | 8 -------- .../scaffold/src/views/w3m-swap-view/index.ts | 17 ++++++++--------- 2 files changed, 8 insertions(+), 17 deletions(-) diff --git a/packages/core/src/controllers/SwapApiController.ts b/packages/core/src/controllers/SwapApiController.ts index 786d52869f..089d97beb8 100644 --- a/packages/core/src/controllers/SwapApiController.ts +++ b/packages/core/src/controllers/SwapApiController.ts @@ -12,9 +12,7 @@ export interface SwapApiControllerState { initialLoading?: boolean isTransactionPending?: boolean sourceToken?: TokenInfo - sourceTokenAddress?: `0x${string}` toToken?: TokenInfo - toTokenAddress?: `0x${string}` toTokenAmount?: string swapTransaction?: TransactionData swapApproval?: SwapApprovalData @@ -89,10 +87,6 @@ type StateKey = keyof SwapApiControllerState // -- State --------------------------------------------- // const state = proxy({ - sourceToken: undefined, - sourceTokenAddress: undefined, - toToken: undefined, - toTokenAddress: undefined, hasAllowance: false, toTokenAmount: undefined, sourceTokenAmount: undefined, @@ -143,7 +137,6 @@ export const SwapApiController = { setSourceToken(sourceToken?: TokenInfo) { state.sourceToken = sourceToken - state.sourceTokenAddress = sourceToken?.address }, setSourceTokenAmount(swapFromAmount: string) { @@ -152,7 +145,6 @@ export const SwapApiController = { setToToken(toToken?: TokenInfo) { state.toToken = toToken - state.toTokenAddress = toToken?.address }, setSlippage(slippage: number) { diff --git a/packages/scaffold/src/views/w3m-swap-view/index.ts b/packages/scaffold/src/views/w3m-swap-view/index.ts index 20024b374e..11a7073aaa 100644 --- a/packages/scaffold/src/views/w3m-swap-view/index.ts +++ b/packages/scaffold/src/views/w3m-swap-view/index.ts @@ -59,6 +59,12 @@ export class W3mSwapView extends LitElement { this.unsubscribe.push( ...[ + SwapApiController.subscribeKey('sourceToken', newSourceToken => { + this.sourceToken = newSourceToken + }), + SwapApiController.subscribeKey('toToken', newToToken => { + this.toToken = newToToken + }), SwapApiController.subscribe(newState => { if (this.loading !== newState.loading) { this.loading = newState.loading @@ -70,18 +76,9 @@ export class W3mSwapView extends LitElement { if (this.sourceTokenAmount !== newState.sourceTokenAmount) { this.sourceTokenAmount = newState.sourceTokenAmount ?? '' } - if (newState.sourceTokenAddress) { - this.sourceToken = newState.sourceToken - } - if (newState.toTokenAddress) { - this.toToken = newState.toToken - } if (this.toTokenAmount !== newState.toTokenAmount) { this.toTokenAmount = newState.toTokenAmount ?? '' } - if (this.sourceToken?.address !== newState.sourceToken?.address) { - this.sourceToken = newState.sourceToken - } if (this.swapErrorMessage !== newState.swapErrorMessage) { this.swapErrorMessage = newState.swapErrorMessage } @@ -153,6 +150,8 @@ export class W3mSwapView extends LitElement { // -- Render -------------------------------------------- // public override render() { + console.log('tokens:', this.sourceToken, this.toToken) + return html` ${this.initialLoading || this.isTransactionPending From 66061e9f4e4de3b353d3f7b8c29b7d1a960d3a72 Mon Sep 17 00:00:00 2001 From: Cali93 <32299095+Cali93@users.noreply.github.com> Date: Fri, 24 Nov 2023 18:21:12 +0200 Subject: [PATCH 08/96] fix(swaps): fix input onChange --- package-lock.json | 48 +++++++++---------- .../scaffold/src/views/w3m-swap-view/index.ts | 24 ++-------- .../ui/src/composites/wui-swap-input/index.ts | 22 ++++++--- 3 files changed, 43 insertions(+), 51 deletions(-) diff --git a/package-lock.json b/package-lock.json index f27c4d7c41..ca630ffead 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11297,9 +11297,9 @@ } }, "node_modules/@storybook/core-common/node_modules/@types/node": { - "version": "18.18.12", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.12.tgz", - "integrity": "sha512-G7slVfkwOm7g8VqcEF1/5SXiMjP3Tbt+pXDU3r/qhlM2KkGm786DUD4xyMA2QzEElFrv/KZV9gjygv4LnkpbMQ==", + "version": "18.18.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.13.tgz", + "integrity": "sha512-vXYZGRrSCreZmq1rEjMRLXJhiy8MrIeVasx+PCVlP414N7CJLHnMf+juVvjdprHyH+XRy3zKZLHeNueOpJCn0g==", "dependencies": { "undici-types": "~5.26.4" } @@ -11528,9 +11528,9 @@ } }, "node_modules/@storybook/core-server/node_modules/@types/node": { - "version": "18.18.12", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.12.tgz", - "integrity": "sha512-G7slVfkwOm7g8VqcEF1/5SXiMjP3Tbt+pXDU3r/qhlM2KkGm786DUD4xyMA2QzEElFrv/KZV9gjygv4LnkpbMQ==", + "version": "18.18.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.13.tgz", + "integrity": "sha512-vXYZGRrSCreZmq1rEjMRLXJhiy8MrIeVasx+PCVlP414N7CJLHnMf+juVvjdprHyH+XRy3zKZLHeNueOpJCn0g==", "dependencies": { "undici-types": "~5.26.4" } @@ -12434,9 +12434,9 @@ "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" }, "node_modules/@types/node": { - "version": "20.9.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.4.tgz", - "integrity": "sha512-wmyg8HUhcn6ACjsn8oKYjkN/zUzQeNtMy44weTJSM6p4MMzEOuKbA3OjJ267uPCOW7Xex9dyrNTful8XTQYoDA==", + "version": "20.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.0.tgz", + "integrity": "sha512-D0WfRmU9TQ8I9PFx9Yc+EBHw+vSpIub4IDvQivcp26PtPrdMGAq5SDcpXEo/epqa/DXotVpekHiLNTg3iaKXBQ==", "dependencies": { "undici-types": "~5.26.4" } @@ -16431,9 +16431,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.590", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.590.tgz", - "integrity": "sha512-hohItzsQcG7/FBsviCYMtQwUSWvVF7NVqPOnJCErWsAshsP/CR2LAXdmq276RbESNdhxiAq5/vRo1g2pxGXVww==" + "version": "1.4.593", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.593.tgz", + "integrity": "sha512-c7+Hhj87zWmdpmjDONbvNKNo24tvmD4mjal1+qqTYTrlF0/sNpAcDlU0Ki84ftA/5yj3BF2QhSGEC0Rky6larg==" }, "node_modules/elliptic": { "version": "6.5.4", @@ -22996,9 +22996,9 @@ } }, "node_modules/node-gyp-build": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.7.0.tgz", - "integrity": "sha512-PbZERfeFdrHQOOXiAKOY0VPbykZy90ndPKk0d+CFDegTKmWp1VgOTz2xACVbr1BjCWxrQp68CXtvNsveFhqDJg==", + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.7.1.tgz", + "integrity": "sha512-wTSrZ+8lsRRa3I3H8Xr65dLWSgCvY2l4AOnaeKdPA9TB/WYMPaTcrzf3rXvFoVvjKNVnu0CcWSx54qq9GKRUYg==", "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", @@ -23828,9 +23828,9 @@ } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.3.tgz", - "integrity": "sha512-B7gr+F6MkqB3uzINHXNctGieGsRTMwIBgxkp0yq/5BwcuDzD4A8wQpHQW6vDAm1uKSLQghmRdD9sKqf2vJ1cEg==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", + "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", "engines": { "node": "14 || >=16.14" } @@ -27174,9 +27174,9 @@ } }, "node_modules/tocbot": { - "version": "4.25.0", - "resolved": "https://registry.npmjs.org/tocbot/-/tocbot-4.25.0.tgz", - "integrity": "sha512-kE5wyCQJ40hqUaRVkyQ4z5+4juzYsv/eK+aqD97N62YH0TxFhzJvo22RUQQZdO3YnXAk42ZOfOpjVdy+Z0YokA==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/tocbot/-/tocbot-4.23.0.tgz", + "integrity": "sha512-5DWuSZXsqG894mkGb8ZsQt9myyQyVxE50AiGRZ0obV0BVUTVkaZmc9jbgpknaAAPUm4FIrzGkEseD6FuQJYJDQ==", "dev": true }, "node_modules/toggle-selection": { @@ -27906,9 +27906,9 @@ } }, "node_modules/unstorage/node_modules/lru-cache": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.3.tgz", - "integrity": "sha512-B7gr+F6MkqB3uzINHXNctGieGsRTMwIBgxkp0yq/5BwcuDzD4A8wQpHQW6vDAm1uKSLQghmRdD9sKqf2vJ1cEg==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", + "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", "engines": { "node": "14 || >=16.14" } diff --git a/packages/scaffold/src/views/w3m-swap-view/index.ts b/packages/scaffold/src/views/w3m-swap-view/index.ts index 11a7073aaa..2602643a45 100644 --- a/packages/scaffold/src/views/w3m-swap-view/index.ts +++ b/packages/scaffold/src/views/w3m-swap-view/index.ts @@ -4,7 +4,6 @@ import { state } from 'lit/decorators.js' import styles from './styles.js' import { SwapApiController, - EventsController, RouterController, CoreHelperUtil, NetworkController @@ -150,8 +149,6 @@ export class W3mSwapView extends LitElement { // -- Render -------------------------------------------- // public override render() { - console.log('tokens:', this.sourceToken, this.toToken) - return html` ${this.initialLoading || this.isTransactionPending @@ -184,7 +181,7 @@ export class W3mSwapView extends LitElement { variant="fullWidth" @click=${this.onSwap.bind(this)} > - Preview swap + Swap `} @@ -241,11 +238,11 @@ export class W3mSwapView extends LitElement {
` } - private templateTokenInput(target: Target, token: TokenInfo | undefined) { + private templateTokenInput(target: Target, token?: TokenInfo) { return html`` @@ -300,21 +297,6 @@ export class W3mSwapView extends LitElement { RouterController.push('PreviewSwap') } - private onSelectToken(target: Target) { - EventsController.sendEvent({ type: 'track', event: 'CLICK_SELECT_TOKEN_TO_SWAP' }) - RouterController.push('SwapSelectToken', { - target - }) - } - - private onFromInputFocus(state: boolean) { - this.fromInputFocus = state - } - - private onToInputFocus(state: boolean) { - this.toInputFocus = state - } - private toggleDetails() { this.detailsOpen = !this.detailsOpen } diff --git a/packages/ui/src/composites/wui-swap-input/index.ts b/packages/ui/src/composites/wui-swap-input/index.ts index e951ed9657..5a1a47f38d 100644 --- a/packages/ui/src/composites/wui-swap-input/index.ts +++ b/packages/ui/src/composites/wui-swap-input/index.ts @@ -7,6 +7,7 @@ import '../wui-transaction-visual/index.js' import { EventsController, RouterController } from '@web3modal/core' import styles from './styles.js' import type { TokenInfo } from '@web3modal/core/dist/types/src/controllers/SwapApiController.js' +import { createRef } from 'lit/directives/ref.js' type Target = 'sourceToken' | 'toToken' @@ -15,7 +16,7 @@ export class WuiSwapInput extends LitElement { public static override styles = [resetStyles, styles] // -- State & Properties -------------------------------- // - @property() public focused: boolean = false + @property() public focused = false @property() public value?: string @@ -23,14 +24,12 @@ export class WuiSwapInput extends LitElement { @property() public target: Target = 'sourceToken' - @property() public token: TokenInfo | undefined + @property() public token?: TokenInfo - @property() public onChange?: (event: InputEvent) => void + public inputElementRef = createRef() // -- Render -------------------------------------------- // public override render() { - console.log('swap-input', this.token, this.target) - return html` @@ -39,6 +38,7 @@ export class WuiSwapInput extends LitElement { @focusout=${() => this.onFocusChange(false)} .value=${this.value} ?disabled=${this.disabled} + @input=${this.dispatchInputChangeEvent.bind(this)} /> ${this.templateTokenSelectButton()} @@ -79,12 +79,22 @@ export class WuiSwapInput extends LitElement { this.focused = state } - private onSelectToken(target: Target) { + private onSelectToken() { EventsController.sendEvent({ type: 'track', event: 'CLICK_SELECT_TOKEN_TO_SWAP' }) RouterController.push('SwapSelectToken', { target: this.target }) } + + private dispatchInputChangeEvent() { + this.dispatchEvent( + new CustomEvent('inputChange', { + detail: this.inputElementRef.value?.value, + bubbles: true, + composed: true + }) + ) + } } declare global { From 7bf805c95359ddeaffffe8fc0f1fc088ecba6330 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Tue, 5 Mar 2024 13:06:58 +0300 Subject: [PATCH 09/96] refactor: update all swap comps - update names from swap to convert - add new convert components - refactor styles with updated designs --- .../composites/wui-token-button.stories.ts | 27 +++ .../src/controllers/ConnectionController.ts | 2 +- .../core/src/controllers/RouterController.ts | 8 +- .../core/src/controllers/SwapApiController.ts | 206 ++++++++++++------ packages/core/src/utils/TypeUtil.ts | 3 +- packages/scaffold/index.ts | 10 +- .../scaffold/src/modal/w3m-router/index.ts | 16 +- .../scaffold/src/partials/w3m-header/index.ts | 8 +- .../src/partials/w3m-swap-input/index.ts | 4 +- .../index.ts | 6 +- .../styles.ts | 0 .../index.ts | 8 +- .../styles.ts | 0 .../w3m-convert-select-token-view/index.ts | 194 +++++++++++++++++ .../w3m-convert-select-token-view/styles.ts | 126 +++++++++++ .../index.ts | 127 ++++++++--- .../styles.ts | 8 +- .../views/w3m-swap-select-token-view/index.ts | 118 ---------- .../w3m-swap-select-token-view/styles.ts | 68 ------ packages/ui/index.ts | 3 +- .../index.ts | 63 ++++-- .../styles.ts | 19 +- .../ui/src/composites/wui-input-text/index.ts | 4 +- .../src/composites/wui-token-button/index.ts | 51 +++++ .../src/composites/wui-token-button/styles.ts | 30 +++ .../composites/wui-token-list-item/styles.ts | 10 +- packages/ui/src/utils/TypeUtil.ts | 1 + 27 files changed, 759 insertions(+), 361 deletions(-) create mode 100644 apps/gallery/stories/composites/wui-token-button.stories.ts rename packages/scaffold/src/views/{w3m-preview-swap-view => w3m-convert-preview-view}/index.ts (97%) rename packages/scaffold/src/views/{w3m-preview-swap-view => w3m-convert-preview-view}/styles.ts (100%) rename packages/scaffold/src/views/{w3m-swap-select-network-view => w3m-convert-select-network-view}/index.ts (91%) rename packages/scaffold/src/views/{w3m-swap-select-network-view => w3m-convert-select-network-view}/styles.ts (100%) create mode 100644 packages/scaffold/src/views/w3m-convert-select-token-view/index.ts create mode 100644 packages/scaffold/src/views/w3m-convert-select-token-view/styles.ts rename packages/scaffold/src/views/{w3m-swap-view => w3m-convert-view}/index.ts (73%) rename packages/scaffold/src/views/{w3m-swap-view => w3m-convert-view}/styles.ts (93%) delete mode 100644 packages/scaffold/src/views/w3m-swap-select-token-view/index.ts delete mode 100644 packages/scaffold/src/views/w3m-swap-select-token-view/styles.ts rename packages/ui/src/composites/{wui-swap-input => wui-convert-input}/index.ts (59%) rename packages/ui/src/composites/{wui-swap-input => wui-convert-input}/styles.ts (90%) create mode 100644 packages/ui/src/composites/wui-token-button/index.ts create mode 100644 packages/ui/src/composites/wui-token-button/styles.ts diff --git a/apps/gallery/stories/composites/wui-token-button.stories.ts b/apps/gallery/stories/composites/wui-token-button.stories.ts new file mode 100644 index 0000000000..34ca1b4b6c --- /dev/null +++ b/apps/gallery/stories/composites/wui-token-button.stories.ts @@ -0,0 +1,27 @@ +import type { Meta } from '@storybook/web-components' +import '@web3modal/ui/src/composites/wui-tag' +import type { WuiTag } from '@web3modal/ui/src/composites/wui-tag' +import { html } from 'lit' + +type Component = Meta + +export default { + title: 'Composites/wui-token-button', + args: { + symbol: 'ETH', + logoURI: 'https://tokens-data.1inch.io/images/0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee.png' + }, + argTypes: { + symbol: { + type: 'string' + }, + logoURI: { + type: 'string' + } + } +} as Component + +export const Default: Component = { + render: args => + html`Recent` +} diff --git a/packages/core/src/controllers/ConnectionController.ts b/packages/core/src/controllers/ConnectionController.ts index 79ba43a858..50af024ff1 100644 --- a/packages/core/src/controllers/ConnectionController.ts +++ b/packages/core/src/controllers/ConnectionController.ts @@ -17,7 +17,7 @@ export interface ConnectionControllerClient { connectWalletConnect: (onUri: (uri: string) => void) => Promise disconnect: () => Promise signMessage: (message: string) => Promise - sendTransaction: (args: SendTransactionArgs) => Promise<{ hash?: string }> + sendTransaction: (args: SendTransactionArgs) => Promise<`0x${string}`> parseUnits: (value: string, decimals: number) => bigint formatUnits: (value: bigint, decimals: number) => string connectExternal?: (options: ConnectExternalOptions) => Promise diff --git a/packages/core/src/controllers/RouterController.ts b/packages/core/src/controllers/RouterController.ts index 9ff4c57de5..c0e53804b8 100644 --- a/packages/core/src/controllers/RouterController.ts +++ b/packages/core/src/controllers/RouterController.ts @@ -36,10 +36,10 @@ export interface RouterControllerState { | 'WhatIsANetwork' | 'WhatIsAWallet' | 'WhatIsABuy' - | 'Swap' - | 'SwapSelectToken' - | 'SwapSelectNetwork' - | 'PreviewSwap' + | 'Convert' + | 'ConvertSelectToken' + | 'ConvertSelectNetwork' + | 'ConvertPreview' history: RouterControllerState['view'][] data?: { connector?: Connector diff --git a/packages/core/src/controllers/SwapApiController.ts b/packages/core/src/controllers/SwapApiController.ts index 089d97beb8..fde13958bf 100644 --- a/packages/core/src/controllers/SwapApiController.ts +++ b/packages/core/src/controllers/SwapApiController.ts @@ -7,6 +7,20 @@ import { AccountController } from './AccountController.js' import { ConstantsUtil } from '../utils/ConstantsUtil.js' import { ConnectionController } from './ConnectionController.js' +const ONEINCH_API_BASE_URL = 'https://1inch-swap-proxy.walletconnect-v1-bridge.workers.dev' + +const OneInchAPIEndpoints = { + approveTransaction: (chainId: number) => `/swap/v5.2/${chainId}/approve/transaction`, + approveAllowance: (chainId: number) => `/swap/v5.2/${chainId}/approve/allowance`, + swap: (chainId: number) => `/swap/v5.2/${chainId}/swap`, + tokens: (chainId: number) => `/swap/v5.2/${chainId}/tokens`, + tokensCustom: (chainId: number) => `/token/v1.2/${chainId}/custom`, + tokensPrices: (chainId: number) => `/price/v1.1/${chainId}`, + search: (chainId: number) => `/token/v1.2/${chainId}/search`, + balance: (chainId: number, address: string | undefined) => + `/balance/v1.2/${chainId}/balances/${address}` +} + // -- Types --------------------------------------------- // export interface SwapApiControllerState { initialLoading?: boolean @@ -25,7 +39,9 @@ export interface SwapApiControllerState { tokens?: Record foundTokens?: TokenInfo[] myTokensWithBalance?: Record + tokensPriceMap: Record swapErrorMessage?: string + loadingPrices: boolean } export interface TokenInfo { @@ -40,6 +56,10 @@ export interface TokenInfo { tags?: string[] } +export interface TokenInfoWithPrice extends TokenInfo { + price: string +} + export interface TokenInfoWithBalance extends TokenInfo { balance: string price: string @@ -98,8 +118,10 @@ const state = proxy({ tokens: undefined, foundTokens: undefined, myTokensWithBalance: undefined, + tokensPriceMap: {}, swapErrorMessage: undefined, - isTransactionPending: false + isTransactionPending: false, + loadingPrices: false }) // -- Controller ---------------------------------------- // @@ -115,9 +137,23 @@ export const SwapApiController = { }, _get1inchApi() { - const baseUrl = 'https://1inch-swap-proxy.walletconnect-v1-bridge.workers.dev' + const api = new FetchUtil({ baseUrl: ONEINCH_API_BASE_URL }) + const chainId = CoreHelperUtil.getEvmChainId(NetworkController.state.caipNetwork?.id) + const { address } = AccountController.state - return new FetchUtil({ baseUrl }) + return { + api, + paths: { + approveTransaction: OneInchAPIEndpoints.approveTransaction(chainId), + approveAllowance: OneInchAPIEndpoints.approveAllowance(chainId), + swap: OneInchAPIEndpoints.swap(chainId), + tokens: OneInchAPIEndpoints.tokens(chainId), + tokensCustom: OneInchAPIEndpoints.tokensCustom(chainId), + tokenPrices: OneInchAPIEndpoints.tokensPrices(chainId), + search: OneInchAPIEndpoints.search(chainId), + balance: OneInchAPIEndpoints.balance(chainId, address) + } + } }, _getSwapParams() { @@ -137,14 +173,24 @@ export const SwapApiController = { setSourceToken(sourceToken?: TokenInfo) { state.sourceToken = sourceToken + if (sourceToken?.address && !state.tokensPriceMap[sourceToken?.address]) { + this.getTokenPriceWithAddresses([sourceToken?.address]) + } + }, + + setSourceTokenAmount(amount: string) { + state.sourceTokenAmount = amount }, - setSourceTokenAmount(swapFromAmount: string) { - state.sourceTokenAmount = swapFromAmount + setToTokenAmount(amount: string) { + state.toTokenAmount = amount }, setToToken(toToken?: TokenInfo) { state.toToken = toToken + if (toToken?.address && !state.tokensPriceMap[toToken?.address]) { + this.getTokenPriceWithAddresses([toToken.address]) + } }, setSlippage(slippage: number) { @@ -186,7 +232,7 @@ export const SwapApiController = { const api = this._get1inchApi() const chainId = CoreHelperUtil.getEvmChainId(NetworkController.state.caipNetwork?.id) - const path = `${api.baseUrl}/swap/v5.2/${chainId}/swap` + const path = OneInchAPIEndpoints.swap(chainId) const { fromAddress, slippage, sourceTokenAddress, sourceTokenAmount, toTokenAddress } = this._getSwapParams() @@ -227,16 +273,15 @@ export const SwapApiController = { }, async getSwapApprovalCalldata() { - const api = this._get1inchApi() - const chainId = CoreHelperUtil.getEvmChainId(NetworkController.state.caipNetwork?.id) - const path = `${api.baseUrl}/swap/v5.2/${chainId}/approve/transaction` + const { api, paths } = this._get1inchApi() const { sourceTokenAddress, sourceTokenAmount } = this._getSwapParams() if (!sourceTokenAmount || !state.sourceToken?.decimals) { return } + const res = await api.get({ - path, + path: paths.approveTransaction, params: { tokenAddress: sourceTokenAddress, amount: ConnectionController.parseUnits( @@ -250,13 +295,11 @@ export const SwapApiController = { }, async getTokenAllowance() { - const api = this._get1inchApi() - const chainId = CoreHelperUtil.getEvmChainId(NetworkController.state.caipNetwork?.id) - const path = `${api.baseUrl}/swap/v5.2/${chainId}/approve/allowance` + const { api, paths } = this._get1inchApi() const { sourceTokenAddress, fromAddress, sourceTokenAmount } = this._getSwapParams() const res = await api.get<{ allowance: string }>({ - path, + path: paths.approveAllowance, params: { tokenAddress: sourceTokenAddress, walletAddress: fromAddress } }) @@ -279,13 +322,9 @@ export const SwapApiController = { return state.tokens } state.initialLoading = true - const api = this._get1inchApi() - const chainId = CoreHelperUtil.getEvmChainId(NetworkController.state.caipNetwork?.id) - const path = `${api.baseUrl}/swap/v5.2/${chainId}/tokens` + const { api, paths } = this._get1inchApi() - const res = await api.get({ - path - }) + const res = await api.get({ path: paths.tokens }) state.tokens = Object.entries(res.tokens) .sort(([, aTokenInfo], [, bTokenInfo]) => { @@ -312,15 +351,11 @@ export const SwapApiController = { }, async searchTokens(searchTerm: string) { - const api = this._get1inchApi() - const chainId = CoreHelperUtil.getEvmChainId(NetworkController.state.caipNetwork?.id) - const path = `${api.baseUrl}/token/v1.2/${chainId}/search` + const { api, paths } = this._get1inchApi() const res = await api.get({ - path, - params: { - query: searchTerm - } + path: paths.search, + params: { query: searchTerm } }) state.foundTokens = res @@ -337,13 +372,38 @@ export const SwapApiController = { state.initialLoading = true - const api = this._get1inchApi() - const chainId = CoreHelperUtil.getEvmChainId(NetworkController.state.caipNetwork?.id) const { fromAddress } = this._getSwapParams() - const balancesPath = `${api.baseUrl}/balance/v1.2/${chainId}/balances/${fromAddress}` + const { balances, tokenAddresses } = await this.getBalances(fromAddress) + + if (!tokenAddresses?.length) { + state.initialLoading = false + + return undefined + } + + const [tokens, tokensPrice] = await Promise.all([ + this.getTokenInfoWithAddresses(tokenAddresses), + this.getTokenPriceWithAddresses(tokenAddresses) + ]) + + const mergedTokensWithBalances = await this.mergeTokenWithBalances( + tokens, + balances, + tokensPrice + ) + + state.myTokensWithBalance = mergedTokensWithBalances + + state.initialLoading = false + + return mergedTokensWithBalances + }, + + async getBalances() { + const { api, paths } = this._get1inchApi() const balances = await api.get>({ - path: balancesPath + path: paths.balance }) const nonEmptyBalances = Object.entries(balances).reduce>( @@ -357,56 +417,60 @@ export const SwapApiController = { {} ) - const tokenAddresses = Object.keys(nonEmptyBalances) + return { balances: nonEmptyBalances, tokenAddresses: Object.keys(nonEmptyBalances) } + }, - if (!tokenAddresses?.length) { - state.initialLoading = false + async getTokenInfoWithAddresses(addresses: Array) { + const { api, paths } = this._get1inchApi() - return undefined - } + return api.get>({ + path: paths.tokensCustom, + params: { addresses: addresses.join(',') } + }) + }, - const tokensPath = `${api.baseUrl}/token/v1.2/${chainId}/custom` - const tokensSpotPricePath = `${api.baseUrl}/price/v1.1/${chainId}` + async getTokenPriceWithAddresses(addresses: Array) { + state.loadingPrices = true - const [tokens, tokensPrice] = await Promise.all([ - api.get>({ - path: tokensPath, - params: { - addresses: tokenAddresses.join(',') - } - }), - api.post>({ - path: tokensSpotPricePath, - body: { - tokens: tokenAddresses, - currency: 'USD' - }, - headers: { - 'content-type': 'application/json' - } - }) - ]) + const { api, paths } = this._get1inchApi() - const mergedTokensWithBalances = Object.entries(tokens).reduce< - Record - >((mergedTokens, [tokenAddress, tokenInfo], i) => { - mergedTokens[tokenAddress] = { - ...tokenInfo, - balance: balances[tokenAddress] ?? '0', - price: tokensPrice[tokenAddress] ?? '0' - } - if (i === 0) { - this.setSourceToken(tokenInfo) + const prices = await api.post>({ + path: paths.tokenPrices, + body: { tokens: addresses, currency: 'USD' }, + headers: { + 'content-type': 'application/json' } + }) - return mergedTokens - }, {}) + Object.entries(prices).forEach(([tokenAddress, price]) => { + state.tokensPriceMap[tokenAddress] = price + }) - state.myTokensWithBalance = mergedTokensWithBalances + state.loadingPrices = false - state.initialLoading = false + return prices + }, - return mergedTokensWithBalances + async mergeTokenWithBalances( + tokens: Record, + balances: Record, + tokensPrice: Record + ) { + return Object.entries(tokens).reduce>( + (mergedTokens, [tokenAddress, tokenInfo], i) => { + mergedTokens[tokenAddress] = { + ...tokenInfo, + balance: balances[tokenAddress] ?? '0', + price: tokensPrice[tokenAddress] ?? '0' + } + if (i === 0) { + this.setSourceToken(tokenInfo) + } + + return mergedTokens + }, + {} + ) }, async getTokenSwapInfo() { diff --git a/packages/core/src/utils/TypeUtil.ts b/packages/core/src/utils/TypeUtil.ts index b97d903696..172ece45e6 100644 --- a/packages/core/src/utils/TypeUtil.ts +++ b/packages/core/src/utils/TypeUtil.ts @@ -326,7 +326,8 @@ export type Event = } } | { - event: 'CLICK_SWAP' + type: 'track' + event: 'CLICK_CONVERT' } | { type: 'track' diff --git a/packages/scaffold/index.ts b/packages/scaffold/index.ts index f8d8d3c75d..e7d031cbfa 100644 --- a/packages/scaffold/index.ts +++ b/packages/scaffold/index.ts @@ -21,11 +21,11 @@ export * from './src/views/w3m-onramp-activity-view/index.js' export * from './src/views/w3m-onramp-fiat-select-view/index.js' export * from './src/views/w3m-onramp-providers-view/index.js' export * from './src/views/w3m-onramp-tokens-select-view/index.js' -export * from './src/views/w3m-swap-view/index.js' -export * from './src/views/w3m-preview-swap-view/index.js' -export * from './src/views/w3m-swap-select-network-view/index.js' -export * from './src/views/w3m-swap-select-token-view/index.js' -export * from './src/views/w3m-swap-view/index.js' +export * from './src/views/w3m-convert-view/index.js' +export * from './src/views/w3m-convert-preview-view/index.js' +export * from './src/views/w3m-convert-select-network-view/index.js' +export * from './src/views/w3m-convert-select-token-view/index.js' +export * from './src/views/w3m-convert-view/index.js' export * from './src/views/w3m-transactions-view/index.js' export * from './src/views/w3m-what-is-a-network-view/index.js' export * from './src/views/w3m-what-is-a-wallet-view/index.js' diff --git a/packages/scaffold/src/modal/w3m-router/index.ts b/packages/scaffold/src/modal/w3m-router/index.ts index ae83708556..407f7f5827 100644 --- a/packages/scaffold/src/modal/w3m-router/index.ts +++ b/packages/scaffold/src/modal/w3m-router/index.ts @@ -117,14 +117,14 @@ export class W3mRouter extends LitElement { return html`` case 'WalletCompatibleNetworks': return html`` - case 'Swap': - return html`` - case 'SwapSelectToken': - return html`` - case 'SwapSelectNetwork': - return html`` - case 'PreviewSwap': - return html`` + case 'Convert': + return html`` + case 'ConvertSelectToken': + return html`` + case 'ConvertSelectNetwork': + return html`` + case 'ConvertPreview': + return html`` default: return html`` } diff --git a/packages/scaffold/src/partials/w3m-header/index.ts b/packages/scaffold/src/partials/w3m-header/index.ts index d8751813dc..f56a6e33fa 100644 --- a/packages/scaffold/src/partials/w3m-header/index.ts +++ b/packages/scaffold/src/partials/w3m-header/index.ts @@ -53,10 +53,10 @@ function headings() { OnRampFiatSelect: 'Select Currency', WalletReceive: 'Receive', WalletCompatibleNetworks: 'Compatible Networks', - Swap: 'Swap', - SwapSelectToken: 'Select Token', - SwapSelectNetwork: 'Select Network', - PreviewSwap: 'Preview Swap' + Convert: 'Convert', + ConvertSelectToken: 'Select Token', + ConvertSelectNetwork: 'Select Network', + ConvertPreview: 'Preview Convert' } } diff --git a/packages/scaffold/src/partials/w3m-swap-input/index.ts b/packages/scaffold/src/partials/w3m-swap-input/index.ts index a529042e66..5509895190 100644 --- a/packages/scaffold/src/partials/w3m-swap-input/index.ts +++ b/packages/scaffold/src/partials/w3m-swap-input/index.ts @@ -76,7 +76,7 @@ export class W3mInputCurrency extends LitElement { const symbol = this.selectedCurrency?.symbol || '' const image = this.currencyImages[symbol] || this.tokenImages[symbol] - return html` + return html` ${this.selectedCurrency ? html` ModalController.open({ view: `OnRamp${this.type}Select` })} > - ${this.selectedCurrency.symbol} + ${this.selectedCurrency.symbol} ` : html``} ` diff --git a/packages/scaffold/src/views/w3m-preview-swap-view/index.ts b/packages/scaffold/src/views/w3m-convert-preview-view/index.ts similarity index 97% rename from packages/scaffold/src/views/w3m-preview-swap-view/index.ts rename to packages/scaffold/src/views/w3m-convert-preview-view/index.ts index f52c723391..6dd25d05c0 100644 --- a/packages/scaffold/src/views/w3m-preview-swap-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-preview-view/index.ts @@ -2,8 +2,8 @@ import { customElement } from '@web3modal/ui' import { LitElement, html } from 'lit' import styles from './styles.js' -@customElement('w3m-preview-swap-view') -export class W3mPreviewSwapView extends LitElement { +@customElement('w3m-convert-preview-view') +export class W3mConvertPreviewView extends LitElement { public static override styles = styles // -- Lifecycle ----------------------------------------- // @@ -107,6 +107,6 @@ export class W3mPreviewSwapView extends LitElement { declare global { interface HTMLElementTagNameMap { - 'w3m-preview-swap-view': W3mPreviewSwapView + 'w3m-convert-preview-view': W3mConvertPreviewView } } diff --git a/packages/scaffold/src/views/w3m-preview-swap-view/styles.ts b/packages/scaffold/src/views/w3m-convert-preview-view/styles.ts similarity index 100% rename from packages/scaffold/src/views/w3m-preview-swap-view/styles.ts rename to packages/scaffold/src/views/w3m-convert-preview-view/styles.ts diff --git a/packages/scaffold/src/views/w3m-swap-select-network-view/index.ts b/packages/scaffold/src/views/w3m-convert-select-network-view/index.ts similarity index 91% rename from packages/scaffold/src/views/w3m-swap-select-network-view/index.ts rename to packages/scaffold/src/views/w3m-convert-select-network-view/index.ts index e2cf9757af..5c8f0fc50d 100644 --- a/packages/scaffold/src/views/w3m-swap-select-network-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-select-network-view/index.ts @@ -27,8 +27,8 @@ const yourItems = [ } ] -@customElement('w3m-swap-select-network-view') -export class W3mSwapSelectNetworkView extends LitElement { +@customElement('w3m-convert-select-network-view') +export class W3mConvertSelectNetworkView extends LitElement { public static override styles = styles // -- Members ------------------------------------------- // private unsubscribe: (() => void)[] = [] @@ -36,8 +36,6 @@ export class W3mSwapSelectNetworkView extends LitElement { // -- State & Properties -------------------------------- // @state() private network = NetworkController.state.caipNetwork - @state() private connected = AccountController.state.isConnected - // -- Lifecycle ----------------------------------------- // public constructor() { super() @@ -95,6 +93,6 @@ export class W3mSwapSelectNetworkView extends LitElement { declare global { interface HTMLElementTagNameMap { - 'w3m-swap-select-network-view': W3mSwapSelectNetworkView + 'w3m-convert-select-network-view': W3mConvertSelectNetworkView } } diff --git a/packages/scaffold/src/views/w3m-swap-select-network-view/styles.ts b/packages/scaffold/src/views/w3m-convert-select-network-view/styles.ts similarity index 100% rename from packages/scaffold/src/views/w3m-swap-select-network-view/styles.ts rename to packages/scaffold/src/views/w3m-convert-select-network-view/styles.ts diff --git a/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts b/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts new file mode 100644 index 0000000000..c27c35ac90 --- /dev/null +++ b/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts @@ -0,0 +1,194 @@ +import { customElement } from '@web3modal/ui' +import { LitElement, html } from 'lit' +import styles from './styles.js' +import { ConnectionController, RouterController, SwapApiController } from '@web3modal/core' +import type { TokenInfo } from '@web3modal/core/src/controllers/SwapApiController.js' +import { state } from 'lit/decorators.js' + +@customElement('w3m-convert-select-token-view') +export class W3mConvertSelectTokenView extends LitElement { + public static override styles = styles + + @state() private targetToken = RouterController.state.data?.target + + // -- Lifecycle ----------------------------------------- // + public constructor() { + super() + } + + private onSelectToken(token: TokenInfo) { + if (this.targetToken === 'sourceToken') { + SwapApiController.setSourceToken(token) + } else { + SwapApiController.setToToken(token) + } + SwapApiController.getTokenSwapInfo() + RouterController.goBack() + } + + public override updated() { + const suggestedTokensContainer = this.renderRoot?.querySelector('.suggested-tokens-container') + suggestedTokensContainer?.addEventListener( + 'scroll', + this.handleSuggestedTokensScroll.bind(this) + ) + + const tokensList = this.renderRoot?.querySelector('.tokens') + tokensList?.addEventListener('scroll', this.handleTokenListScroll.bind(this)) + } + + // -- Render -------------------------------------------- // + public override render() { + return html` + + ${this.templateSearchInput()} ${this.templateSuggestedTokens()} ${this.templateTokens()} + + ` + } + + // -- Private ------------------------------------------- // + private templateSearchInput() { + return html` + + + + ` + } + + private templateTokens() { + return html` + + + ${SwapApiController.state.myTokensWithBalance && + html` + Your tokens + `} + + + ${SwapApiController.state.myTokensWithBalance && + Object.values(SwapApiController.state.myTokensWithBalance).map( + tokenInfo => html` + this.onSelectToken(tokenInfo)} + > + + ` + )} + + + Popular tokens + + + ${SwapApiController.state.tokens && + Object.values(SwapApiController.state.tokens).map( + tokenInfo => html` + this.onSelectToken(tokenInfo)} + > + + ` + )} + + + + ` + } + + private templateSuggestedTokens() { + const tokens = SwapApiController.state.tokens + ? Object.values(SwapApiController.state.tokens).slice(0, 8) + : null + + if (!tokens) { + return null + } + + return html` + + ${tokens.map( + tokenInfo => html` + this.onSelectToken(tokenInfo)} + > + + ` + )} + + ` + } + + private handleSuggestedTokensScroll() { + const container = this.renderRoot?.querySelector('.suggested-tokens-container') as HTMLElement + + const scrollStart = container.scrollLeft === 0 + const scrollEnd = + Math.abs(Math.round(container.scrollLeft + container.offsetWidth) - container.scrollWidth) < 2 + + if (scrollStart) { + container.classList.add('scroll-start') + container.classList.remove('scroll-end') + } else if (scrollEnd) { + container.classList.add('scroll-end') + container.classList.remove('scroll-start') + } else { + container.classList.remove('scroll-start') + container.classList.remove('scroll-end') + } + } + + private handleTokenListScroll() { + const container = this.renderRoot?.querySelector('.tokens') as HTMLElement + + const scrollStart = container.scrollTop === 0 + const scrollEnd = + Math.abs(Math.round(container.scrollTop + container.offsetHeight) - container.scrollHeight) < + 2 + + if (scrollStart) { + container.classList.add('scroll-start') + container.classList.remove('scroll-end') + } else if (scrollEnd) { + container.classList.add('scroll-end') + container.classList.remove('scroll-start') + } else { + container.classList.remove('scroll-start') + container.classList.remove('scroll-end') + } + } + + public override disconnectedCallback() { + super.disconnectedCallback() + const suggestedTokensContainer = this.renderRoot?.querySelector('.suggested-tokens-container') + const tokensList = this.renderRoot?.querySelector('.tokens') + + suggestedTokensContainer?.removeEventListener( + 'scroll', + this.handleSuggestedTokensScroll.bind(this) + ) + tokensList?.removeEventListener('scroll', this.handleTokenListScroll.bind(this)) + } +} + +declare global { + interface HTMLElementTagNameMap { + 'w3m-convert-select-token-view': W3mConvertSelectTokenView + } +} diff --git a/packages/scaffold/src/views/w3m-convert-select-token-view/styles.ts b/packages/scaffold/src/views/w3m-convert-select-token-view/styles.ts new file mode 100644 index 0000000000..370b9a7717 --- /dev/null +++ b/packages/scaffold/src/views/w3m-convert-select-token-view/styles.ts @@ -0,0 +1,126 @@ +import { css } from 'lit' + +export default css` + :host > wui-flex:first-child { + overflow-y: hidden; + overflow-x: hidden; + scrollbar-width: none; + scrollbar-height: none; + } + + wui-loading-hexagon { + position: absolute; + } + + .search-input-container, + .suggested-tokens-container, + .tokens-container { + padding-left: var(--wui-spacing-s); + padding-right: var(--wui-spacing-s); + } + + .search-input-container { + padding-top: var(--wui-spacing-s); + } + + .suggested-tokens-container { + overflow-x: auto; + -webkit-mask-image: linear-gradient( + to right, + transparent 0px, + black 44px, + black calc(100% - 44px), + transparent 100% + ); + mask-image: linear-gradient( + to right, + transparent 0px, + black 44px, + black calc(100% - 44px), + transparent 100% + ); + } + + .suggested-tokens-container::-webkit-scrollbar { + display: none; + } + + .suggested-tokens-container.scroll-start { + -webkit-mask-image: linear-gradient( + to right, + black 0px, + black calc(100% - 44px), + transparent 100% + ); + mask-image: linear-gradient(to right, black 0px, black calc(100% - 44px), transparent 100%); + } + + .suggested-tokens-container.scroll-end { + -webkit-mask-image: linear-gradient(to right, transparent 0px, black 44px, black 100%); + mask-image: linear-gradient(to right, transparent 0px, black 44px, black 100%); + } + + .tokens-container { + border-top: 1px solid var(--wui-gray-glass-005); + height: 100%; + max-height: calc(512px); + } + + .tokens { + width: 100%; + overflow-y: auto; + -webkit-mask-image: linear-gradient( + transparent 0px, + black 44px, + black calc(100% - 44px), + transparent 100% + ); + mask-image: linear-gradient( + transparent 0px, + black 44px, + black calc(100% - 44px), + transparent 100% + ); + } + + .tokens.scroll-start { + -webkit-mask-image: linear-gradient(black 0px, black calc(100% - 44px), transparent 100%); + mask-image: linear-gradient(black 0px, black calc(100% - 44px), transparent 100%); + } + + .tokens.scroll-end { + -webkit-mask-image: linear-gradient(transparent 0px, black 44px, black 100%); + mask-image: linear-gradient(transparent 0px, black 44px, black 100%); + } + + .network-search-input, + .select-network-button { + height: 40px; + } + + .select-network-button { + border: none; + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + gap: var(--wui-spacing-xs); + box-shadow: inset 0 0 0 1px var(--wui-gray-glass-005); + background-color: transparent; + border-radius: var(--wui-border-radius-xxs); + padding: var(--wui-spacing-xs); + align-items: center; + transition: background-color 0.2s linear; + } + + .select-network-button:hover { + background-color: var(--wui-gray-glass-002); + } + + .select-network-button > wui-image { + width: 26px; + height: 26px; + border-radius: var(--wui-border-radius-xs); + box-shadow: inset 0 0 0 1px var(--wui-gray-glass-010); + } +` diff --git a/packages/scaffold/src/views/w3m-swap-view/index.ts b/packages/scaffold/src/views/w3m-convert-view/index.ts similarity index 73% rename from packages/scaffold/src/views/w3m-swap-view/index.ts rename to packages/scaffold/src/views/w3m-convert-view/index.ts index 2602643a45..c8d586e276 100644 --- a/packages/scaffold/src/views/w3m-swap-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-view/index.ts @@ -6,14 +6,15 @@ import { SwapApiController, RouterController, CoreHelperUtil, - NetworkController + NetworkController, + ConnectionController } from '@web3modal/core' import type { TokenInfo } from '@web3modal/core/src/controllers/SwapApiController.js' type Target = 'sourceToken' | 'toToken' -@customElement('w3m-swap-view') -export class W3mSwapView extends LitElement { +@customElement('w3m-convert-view') +export class W3mConvertView extends LitElement { public static override styles = styles private unsubscribe: ((() => void) | undefined)[] = [] @@ -25,6 +26,8 @@ export class W3mSwapView extends LitElement { @state() private loading = SwapApiController.state.loading + @state() private loadingPrices = SwapApiController.state.loadingPrices + @state() private sourceToken = SwapApiController.state.sourceToken @state() private toToken = SwapApiController.state.toToken @@ -65,9 +68,13 @@ export class W3mSwapView extends LitElement { this.toToken = newToToken }), SwapApiController.subscribe(newState => { + console.log('>>> newState', newState.tokensPriceMap) if (this.loading !== newState.loading) { this.loading = newState.loading } + if (this.loadingPrices !== newState.loadingPrices) { + this.loadingPrices = newState.loadingPrices + } if (this.initialLoading !== newState.initialLoading) { this.initialLoading = newState.initialLoading } @@ -107,21 +114,19 @@ export class W3mSwapView extends LitElement { return null } - private onInputChange(event: InputEvent) { - const inputElement = event.target as HTMLElement - const input = this.getInputElement(inputElement) - - SwapApiController.clearError() - - if (input) { - SwapApiController.setSourceTokenAmount(input.value) - this.onDebouncedGetSwapCalldata() + private handleChangeAmount(target: Target, value: string) { + if (target === 'sourceToken') { + SwapApiController.setSourceTokenAmount(value) + } else { + SwapApiController.setToTokenAmount(value) } + SwapApiController.clearError() + this.onDebouncedGetSwapCalldata() } private onDebouncedGetSwapCalldata = CoreHelperUtil.debounce(async () => { await SwapApiController.getTokenSwapInfo() - }, 2000) + }, 500) private async onSwap() { await SwapApiController.swapTokens() @@ -149,6 +154,8 @@ export class W3mSwapView extends LitElement { // -- Render -------------------------------------------- // public override render() { + console.log('>>> toToken', this.toToken) + return html` ${this.initialLoading || this.isTransactionPending @@ -164,7 +171,12 @@ export class W3mSwapView extends LitElement { return html` - + ${this.templateTokenInput('sourceToken', this.sourceToken)} ${this.templateTokenInput('toToken', this.toToken)} ${this.templateReplaceTokensButton()} @@ -172,17 +184,18 @@ export class W3mSwapView extends LitElement { ${this.templateDetails()} - ${haveNoTokenSelected - ? html` - Select token - ` - : html` - Swap - `} + + Swap + ` @@ -238,26 +251,72 @@ export class W3mSwapView extends LitElement { ` } + handleValueChange(e) { + // Update your state based on the event + // You might need additional logic to know whether it's toTokenAmount or sourceTokenAmount + this.toTokenAmount = e.detail // or this.sourceTokenAmount, depending on your logic + } + private templateTokenInput(target: Target, token?: TokenInfo) { - return html`` + .balance=${myToken?.balance} + .marketValue=${marketValue} + amount=${myToken + ? parseFloat( + ConnectionController.formatUnits(BigInt(myToken.balance), myToken.decimals) + ).toFixed(3) + : 0} + >` } private templateDetails() { + const sourceTokenPrice = parseFloat( + SwapApiController.state.tokensPriceMap?.[this.sourceToken?.address ?? ''] || '0' + ) + const sourceTokenPriceString = parseFloat( + SwapApiController.state.tokensPriceMap?.[this.sourceToken?.address ?? ''] || '0' + ).toLocaleString('en-US', { + maximumFractionDigits: 2, + minimumFractionDigits: 2 + }) + const toTokenPrice = parseFloat( + SwapApiController.state.tokensPriceMap?.[this.toToken?.address ?? ''] || '0' + ) + const sourceTokenEquivalent = ( + sourceTokenPrice && toTokenPrice ? (1 / toTokenPrice) * sourceTokenPrice : 0 + ).toLocaleString('en-US', { + maximumFractionDigits: 4, + minimumFractionDigits: 4 + }) + return html` ${this.detailsOpen @@ -293,8 +352,8 @@ export class W3mSwapView extends LitElement { ` } - private onPreviewSwap() { - RouterController.push('PreviewSwap') + private onConvertPreview() { + RouterController.push('ConvertPreview') } private toggleDetails() { @@ -304,6 +363,6 @@ export class W3mSwapView extends LitElement { declare global { interface HTMLElementTagNameMap { - 'w3m-swap-view': W3mSwapView + 'w3m-convert-view': W3mConvertView } } diff --git a/packages/scaffold/src/views/w3m-swap-view/styles.ts b/packages/scaffold/src/views/w3m-convert-view/styles.ts similarity index 93% rename from packages/scaffold/src/views/w3m-swap-view/styles.ts rename to packages/scaffold/src/views/w3m-convert-view/styles.ts index 80fab1ebee..ffa565156a 100644 --- a/packages/scaffold/src/views/w3m-swap-view/styles.ts +++ b/packages/scaffold/src/views/w3m-convert-view/styles.ts @@ -16,13 +16,11 @@ export default css` border-radius: var(--wui-border-radius-xs); } - .action-button button { - width: 100%; - height: 48px; - border-radius: var(--wui-border-radius-xs); + .action-button:disabled { + border-color: 1px solid var(--wui-gray-glass-002); } - .swap-inputs-container { + .convert-inputs-container { position: relative; } diff --git a/packages/scaffold/src/views/w3m-swap-select-token-view/index.ts b/packages/scaffold/src/views/w3m-swap-select-token-view/index.ts deleted file mode 100644 index 6e0e640669..0000000000 --- a/packages/scaffold/src/views/w3m-swap-select-token-view/index.ts +++ /dev/null @@ -1,118 +0,0 @@ -import { customElement } from '@web3modal/ui' -import { LitElement, html } from 'lit' -import styles from './styles.js' -import { ConnectionController, RouterController, SwapApiController } from '@web3modal/core' -import type { TokenInfo } from '@web3modal/core/src/controllers/SwapApiController.js' -import { state } from 'lit/decorators.js' - -import { EventsController } from '@web3modal/core' - -@customElement('w3m-swap-select-token-view') -export class W3mSwapSelectTokenView extends LitElement { - public static override styles = styles - - @state() private targetToken = RouterController.state.data?.target - - // -- Lifecycle ----------------------------------------- // - public constructor() { - super() - } - - private onSelectToken(token: TokenInfo) { - if (this.targetToken === 'sourceToken') { - SwapApiController.setSourceToken(token) - } else { - SwapApiController.setToToken(token) - } - SwapApiController.getTokenSwapInfo() - RouterController.goBack() - } - - // -- Render -------------------------------------------- // - public override render() { - return html` - - ${this.templateSearchInput()} ${this.templateListTokens()} - - ` - } - - // -- Private ------------------------------------------- // - private templateSearchInput() { - return html` - - - - - ` - } - - private templateListTokens() { - return html` - - ${SwapApiController.state.myTokensWithBalance && - html` - Your tokens - `} - - - ${SwapApiController.state.myTokensWithBalance && - Object.values(SwapApiController.state.myTokensWithBalance).map( - tokenInfo => html` - this.onSelectToken(tokenInfo)} - > - - ` - )} - - - Popular tokens - - - ${SwapApiController.state.tokens && - Object.values(SwapApiController.state.tokens).map( - tokenInfo => html` - this.onSelectToken(tokenInfo)} - > - - ` - )} - - - ` - } - - private onSelectNetwork() { - EventsController.sendEvent({ type: 'track', event: 'CLICK_SELECT_NETWORK_TO_SWAP' }) - RouterController.push('SwapSelectNetwork') - } -} - -declare global { - interface HTMLElementTagNameMap { - 'w3m-swap-select-token-view': W3mSwapSelectTokenView - } -} diff --git a/packages/scaffold/src/views/w3m-swap-select-token-view/styles.ts b/packages/scaffold/src/views/w3m-swap-select-token-view/styles.ts deleted file mode 100644 index d690ca7a98..0000000000 --- a/packages/scaffold/src/views/w3m-swap-select-token-view/styles.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { css } from 'lit' - -export default css` - :host > wui-flex:first-child { - overflow-y: hidden; - overflow-x: hidden; - scrollbar-width: none; - } - - wui-loading-hexagon { - position: absolute; - } - - .token-list { - padding-top: var(--wui-spacing-s); - max-height: calc(512px); - overflow-y: auto; - -webkit-mask-image: linear-gradient( - transparent 0px, - transparent 8px, - black 24px, - black 25px, - black 32px, - black 100% - ); - mask-image: linear-gradient( - transparent 0px, - transparent 8px, - black 24px, - black 25px, - black 32px, - black 100% - ); - border-top-left-radius: 30px; - border-top-right-radius: 30px; - } - - .network-search-input, - .select-network-button { - height: 40px; - } - - .select-network-button { - border: none; - display: flex; - flex-direction: row; - align-items: center; - justify-content: center; - gap: var(--wui-spacing-xs); - box-shadow: inset 0 0 0 1px var(--wui-gray-glass-005); - background-color: transparent; - border-radius: var(--wui-border-radius-xxs); - padding: var(--wui-spacing-xs); - align-items: center; - transition: background-color 0.2s linear; - } - - .select-network-button:hover { - background-color: var(--wui-gray-glass-002); - } - - .select-network-button > wui-image { - width: 26px; - height: 26px; - border-radius: var(--wui-border-radius-xs); - box-shadow: inset 0 0 0 1px var(--wui-gray-glass-010); - } -` diff --git a/packages/ui/index.ts b/packages/ui/index.ts index 5baea5847b..6f77cf9eaa 100644 --- a/packages/ui/index.ts +++ b/packages/ui/index.ts @@ -36,8 +36,9 @@ export * from './src/composites/wui-otp/index.js' export * from './src/composites/wui-qr-code/index.js' export * from './src/composites/wui-search-bar/index.js' export * from './src/composites/wui-snackbar/index.js' -export * from './src/composites/wui-swap-input/index.js' +export * from './src/composites/wui-convert-input/index.js' export * from './src/composites/wui-tabs/index.js' +export * from './src/composites/wui-token-button/index.js' export * from './src/composites/wui-tag/index.js' export * from './src/composites/wui-tooltip/index.js' export * from './src/composites/wui-token-list-item/index.js' diff --git a/packages/ui/src/composites/wui-swap-input/index.ts b/packages/ui/src/composites/wui-convert-input/index.ts similarity index 59% rename from packages/ui/src/composites/wui-swap-input/index.ts rename to packages/ui/src/composites/wui-convert-input/index.ts index 5a1a47f38d..2358db56fd 100644 --- a/packages/ui/src/composites/wui-swap-input/index.ts +++ b/packages/ui/src/composites/wui-convert-input/index.ts @@ -7,12 +7,11 @@ import '../wui-transaction-visual/index.js' import { EventsController, RouterController } from '@web3modal/core' import styles from './styles.js' import type { TokenInfo } from '@web3modal/core/dist/types/src/controllers/SwapApiController.js' -import { createRef } from 'lit/directives/ref.js' type Target = 'sourceToken' | 'toToken' -@customElement('wui-swap-input') -export class WuiSwapInput extends LitElement { +@customElement('wui-convert-input') +export class WuiConvertInput extends LitElement { public static override styles = [resetStyles, styles] // -- State & Properties -------------------------------- // @@ -20,26 +19,40 @@ export class WuiSwapInput extends LitElement { @property() public value?: string + @property() public marketValue?: string = '$1.0345,00' + + @property() public amount?: string + @property() public disabled?: boolean @property() public target: Target = 'sourceToken' @property() public token?: TokenInfo - public inputElementRef = createRef() + @property() public onSetAmount: (target: Target, value: string) => void = () => {} // -- Render -------------------------------------------- // public override render() { return html` - + this.onFocusChange(true)} @focusout=${() => this.onFocusChange(false)} .value=${this.value} ?disabled=${this.disabled} @input=${this.dispatchInputChangeEvent.bind(this)} + placeholder="0" /> + ${this.value + ? html`$${this.marketValue}` + : null} ${this.templateTokenSelectButton()} @@ -47,6 +60,12 @@ export class WuiSwapInput extends LitElement { } // -- Private ------------------------------------------- // + private setMaxValueToInput() { + if (this.amount?.toString()) { + this.onSetAmount(this.target, this.amount?.toString()) + } + } + private templateTokenSelectButton() { if (!this.token) { return html` @@ -66,12 +85,25 @@ export class WuiSwapInput extends LitElement { ` return html` -
- -
+ ${this.target === 'sourceToken' && this.amount && parseFloat(this.amount) + ? html` + ${this.amount} + + ` + : null} +
` } @@ -81,24 +113,19 @@ export class WuiSwapInput extends LitElement { private onSelectToken() { EventsController.sendEvent({ type: 'track', event: 'CLICK_SELECT_TOKEN_TO_SWAP' }) - RouterController.push('SwapSelectToken', { + RouterController.push('ConvertSelectToken', { target: this.target }) } - private dispatchInputChangeEvent() { - this.dispatchEvent( - new CustomEvent('inputChange', { - detail: this.inputElementRef.value?.value, - bubbles: true, - composed: true - }) - ) + private dispatchInputChangeEvent(event: Event) { + const input = event.target as HTMLInputElement + this.onSetAmount(this.target, input.value) } } declare global { interface HTMLElementTagNameMap { - 'wui-swap-input': WuiSwapInput + 'wui-convert-input': WuiConvertInput } } diff --git a/packages/ui/src/composites/wui-swap-input/styles.ts b/packages/ui/src/composites/wui-convert-input/styles.ts similarity index 90% rename from packages/ui/src/composites/wui-swap-input/styles.ts rename to packages/ui/src/composites/wui-convert-input/styles.ts index 0ad6511475..30f96186cc 100644 --- a/packages/ui/src/composites/wui-swap-input/styles.ts +++ b/packages/ui/src/composites/wui-convert-input/styles.ts @@ -12,6 +12,7 @@ export default css` padding: var(--wui-spacing-xl); padding-right: var(--wui-spacing-s); width: 100%; + height: 100px; box-sizing: border-box; position: relative; transition: background 0.2s linear; @@ -65,14 +66,7 @@ export default css` margin: 0; } - .token-select-button-container { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - } - - .token-select-button-container button { + .token-select-button { display: flex; align-items: center; justify-content: center; @@ -88,7 +82,7 @@ export default css` transition: background 0.2s linear; } - .token-select-button-container button:hover { + .token-select-button:hover { background: var(--wui-gray-glass-005); } @@ -98,4 +92,11 @@ export default css` border-radius: var(--wui-border-radius-s); box-shadow: inset 0 0 0 1px var(--wui-gray-glass-010); } + + .max-value-button { + background-color: transparent; + border: none; + cursor: pointer; + color: var(--wui-gray-glass-020); + } ` diff --git a/packages/ui/src/composites/wui-input-text/index.ts b/packages/ui/src/composites/wui-input-text/index.ts index 665a6a675d..e72fbfe2dd 100644 --- a/packages/ui/src/composites/wui-input-text/index.ts +++ b/packages/ui/src/composites/wui-input-text/index.ts @@ -24,13 +24,11 @@ export class WuiInputText extends LitElement { @property() public placeholder = '' - @property() public value = '' - @property() public type: InputType = 'text' @property() public keyHint?: HTMLInputElement['enterKeyHint'] - @property() public value?: string + @property() public value?: string = '' // -- Render -------------------------------------------- // public override render() { diff --git a/packages/ui/src/composites/wui-token-button/index.ts b/packages/ui/src/composites/wui-token-button/index.ts new file mode 100644 index 0000000000..c88a1387e6 --- /dev/null +++ b/packages/ui/src/composites/wui-token-button/index.ts @@ -0,0 +1,51 @@ +import { html, LitElement } from 'lit' +import { property } from 'lit/decorators.js' +import { customElement } from '../../utils/WebComponentsUtil.js' +import { resetStyles } from '../../utils/ThemeUtil.js' +import styles from './styles.js' +import type { TokenInfo } from '@web3modal/core/dist/types/src/controllers/SwapApiController.js' + +@customElement('wui-token-button') +export class WuiTokenButton extends LitElement { + public static override styles = [resetStyles, styles] + + // -- State & Properties -------------------------------- // + + @property() public logoURI?: string + + @property() public symbol?: string + + @property() public onClick: (token: TokenInfo) => void = () => {} + + // -- Render -------------------------------------------- // + public override render() { + const tokenElement = this.logoURI + ? html`` + : html` + + ` + + return html` + + ` + } +} + +declare global { + interface HTMLElementTagNameMap { + 'wui-token-button': WuiTokenButton + } +} diff --git a/packages/ui/src/composites/wui-token-button/styles.ts b/packages/ui/src/composites/wui-token-button/styles.ts new file mode 100644 index 0000000000..badac76890 --- /dev/null +++ b/packages/ui/src/composites/wui-token-button/styles.ts @@ -0,0 +1,30 @@ +import { css } from 'lit' + +export default css` + :host > button { + display: flex; + align-items: center; + justify-content: center; + gap: var(--wui-spacing-xxs); + padding: var(--wui-spacing-xs); + padding-right: var(--wui-spacing-1xs); + height: 40px; + border: none; + border-radius: 80px; + background: var(--wui-gray-glass-002); + box-shadow: inset 0 0 0 1px var(--wui-gray-glass-002); + cursor: pointer; + transition: background 0.2s linear; + } + + :host > button:hover { + background: var(--wui-gray-glass-005); + } + + :host > button wui-image { + width: 24px; + height: 24px; + border-radius: var(--wui-border-radius-s); + box-shadow: inset 0 0 0 1px var(--wui-gray-glass-010); + } +` diff --git a/packages/ui/src/composites/wui-token-list-item/styles.ts b/packages/ui/src/composites/wui-token-list-item/styles.ts index c1530c5b66..bb4732624e 100644 --- a/packages/ui/src/composites/wui-token-list-item/styles.ts +++ b/packages/ui/src/composites/wui-token-list-item/styles.ts @@ -24,8 +24,16 @@ export default css` :host > wui-flex > wui-image { width: 40px; height: 40px; - box-shadow: 0 0 0 2px var(--wui-gray-glass-005); border-radius: var(--wui-border-radius-3xl); + position: relative; + } + + :host > wui-flex > wui-image::after { + position: absolute; + content: ''; + inset: 0; + box-shadow: inset 0 0 0 1px var(--wui-gray-glass-010); + border-radius: var(--wui-border-radius-l); } button > wui-icon-box[data-variant='square-blue'] { diff --git a/packages/ui/src/utils/TypeUtil.ts b/packages/ui/src/utils/TypeUtil.ts index af7e1b72b2..ec8682f04d 100644 --- a/packages/ui/src/utils/TypeUtil.ts +++ b/packages/ui/src/utils/TypeUtil.ts @@ -10,6 +10,7 @@ export type ColorType = | 'inverse-100' | 'success-100' | 'glass-005' + | 'glass-020' export type TextType = | 'large-500' From 671cdd7ef42bb6a15cf500c9cc908197813a8b49 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Tue, 5 Mar 2024 15:02:26 +0300 Subject: [PATCH 10/96] chore: update styles, remove unused functions --- .../stories/composites/wui-button.stories.ts | 7 +- .../core/src/controllers/SwapApiController.ts | 11 ++- .../w3m-account-default-widget/index.ts | 14 ++++ .../src/views/w3m-convert-view/index.ts | 70 +++++-------------- .../src/views/w3m-convert-view/styles.ts | 2 +- .../composites/wui-convert-input/styles.ts | 2 +- 6 files changed, 44 insertions(+), 62 deletions(-) diff --git a/apps/gallery/stories/composites/wui-button.stories.ts b/apps/gallery/stories/composites/wui-button.stories.ts index 31758c2f93..00512a4282 100644 --- a/apps/gallery/stories/composites/wui-button.stories.ts +++ b/apps/gallery/stories/composites/wui-button.stories.ts @@ -13,19 +13,23 @@ export default { size: 'md', variant: 'fill', disabled: false, + fullWidth: false, iconLeft: undefined, iconRight: undefined, loading: false }, argTypes: { size: { - options: ['sm', 'md'], + options: ['xs', 'sm', 'md', 'lg'], control: { type: 'select' } }, variant: { options: buttonOptions, control: { type: 'select' } }, + fullWidth: { + control: { type: 'boolean' } + }, disabled: { control: { type: 'boolean' } }, @@ -49,6 +53,7 @@ export const Default: Component = { size=${args.size} ?loading=${args.loading} ?disabled=${args.disabled} + ?fullWidth=${args.fullWidth} variant=${args.variant} > ${args.iconLeft diff --git a/packages/core/src/controllers/SwapApiController.ts b/packages/core/src/controllers/SwapApiController.ts index fde13958bf..b62c9476d7 100644 --- a/packages/core/src/controllers/SwapApiController.ts +++ b/packages/core/src/controllers/SwapApiController.ts @@ -322,7 +322,9 @@ export const SwapApiController = { return state.tokens } state.initialLoading = true + const { api, paths } = this._get1inchApi() + await this.getMyTokensWithBalance({ forceRefetch: true }) const res = await api.get({ path: paths.tokens }) @@ -362,18 +364,13 @@ export const SwapApiController = { }, async getMyTokensWithBalance(options?: { forceRefetch?: boolean }) { - if (state.initialLoading) { - return state.myTokensWithBalance - } - - if (state.myTokensWithBalance && !options?.forceRefetch) { + if (!options?.forceRefetch) { return state.myTokensWithBalance } state.initialLoading = true - const { fromAddress } = this._getSwapParams() - const { balances, tokenAddresses } = await this.getBalances(fromAddress) + const { balances, tokenAddresses } = await this.getBalances() if (!tokenAddresses?.length) { state.initialLoading = false diff --git a/packages/scaffold/src/partials/w3m-account-default-widget/index.ts b/packages/scaffold/src/partials/w3m-account-default-widget/index.ts index ca920b6815..66257bf94b 100644 --- a/packages/scaffold/src/partials/w3m-account-default-widget/index.ts +++ b/packages/scaffold/src/partials/w3m-account-default-widget/index.ts @@ -145,6 +145,15 @@ export class W3mAccountDefaultWidget extends LitElement { > Activity + + Convert + unsubscribe?.()) } - private getInputElement(el: HTMLElement) { - if (el.shadowRoot?.querySelector('input')) { - return el.shadowRoot.querySelector('input') - } - - return null - } - - private handleChangeAmount(target: Target, value: string) { - if (target === 'sourceToken') { - SwapApiController.setSourceTokenAmount(value) - } else { - SwapApiController.setToTokenAmount(value) - } - SwapApiController.clearError() - this.onDebouncedGetSwapCalldata() - } - - private onDebouncedGetSwapCalldata = CoreHelperUtil.debounce(async () => { - await SwapApiController.getTokenSwapInfo() - }, 500) - - private async onSwap() { - await SwapApiController.swapTokens() - } - - private async onApprove() { - await SwapApiController.approveSwapTokens() - } - - private onSwitchTokens() { - SwapApiController.switchTokens() - } - - private get actionButtonLabel(): string { - if (!this.toToken || !this.sourceToken) { - return 'Pick tokens' - } - - if (!this.toTokenAmount || !this.sourceTokenAmount) { - return 'Enter Amount' - } - - return this.hasAllowance ? 'Swap' : 'Approve' - } - // -- Render -------------------------------------------- // public override render() { console.log('>>> toToken', this.toToken) @@ -251,12 +205,6 @@ export class W3mConvertView extends LitElement {
` } - handleValueChange(e) { - // Update your state based on the event - // You might need additional logic to know whether it's toTokenAmount or sourceTokenAmount - this.toTokenAmount = e.detail // or this.sourceTokenAmount, depending on your logic - } - private templateTokenInput(target: Target, token?: TokenInfo) { const myToken = SwapApiController.state.myTokensWithBalance?.[token?.address ?? ''] const price = SwapApiController.state.tokensPriceMap?.[token?.address ?? ''] @@ -352,6 +300,24 @@ export class W3mConvertView extends LitElement { ` } + private handleChangeAmount(target: Target, value: string) { + if (target === 'sourceToken') { + SwapApiController.setSourceTokenAmount(value) + } else { + SwapApiController.setToTokenAmount(value) + } + SwapApiController.clearError() + this.onDebouncedGetSwapCalldata() + } + + private onDebouncedGetSwapCalldata = CoreHelperUtil.debounce(async () => { + await SwapApiController.getTokenSwapInfo() + }, 500) + + private onSwitchTokens() { + SwapApiController.switchTokens() + } + private onConvertPreview() { RouterController.push('ConvertPreview') } diff --git a/packages/scaffold/src/views/w3m-convert-view/styles.ts b/packages/scaffold/src/views/w3m-convert-view/styles.ts index ffa565156a..7f3edcae2f 100644 --- a/packages/scaffold/src/views/w3m-convert-view/styles.ts +++ b/packages/scaffold/src/views/w3m-convert-view/styles.ts @@ -17,7 +17,7 @@ export default css` } .action-button:disabled { - border-color: 1px solid var(--wui-gray-glass-002); + border-color: 1px solid var(--wui-gray-glass-005); } .convert-inputs-container { diff --git a/packages/ui/src/composites/wui-convert-input/styles.ts b/packages/ui/src/composites/wui-convert-input/styles.ts index 30f96186cc..08a8cd2318 100644 --- a/packages/ui/src/composites/wui-convert-input/styles.ts +++ b/packages/ui/src/composites/wui-convert-input/styles.ts @@ -7,7 +7,7 @@ export default css` justify-content: space-between; align-items: center; background: var(--wui-gray-glass-002); - border: 1px solid var(--wui-gray-glass-002); + border: 1px solid var(--wui-gray-glass-005); border-radius: var(--wui-border-radius-s); padding: var(--wui-spacing-xl); padding-right: var(--wui-spacing-s); From b00813db2d401f92231fa8cfb6fab98168d3b57a Mon Sep 17 00:00:00 2001 From: enesozturk Date: Wed, 6 Mar 2024 00:50:22 +0300 Subject: [PATCH 11/96] refactor: add amount input validation and formatting --- .../composites/wui-token-button.stories.ts | 2 +- .../core/src/controllers/SwapApiController.ts | 28 ++++- .../scaffold/src/modal/w3m-router/index.ts | 2 +- .../views/w3m-convert-preview-view/index.ts | 106 ++++++++++++++---- .../views/w3m-convert-preview-view/styles.ts | 4 + .../w3m-convert-select-token-view/index.ts | 2 +- .../src/views/w3m-convert-view/index.ts | 23 +++- .../src/composites/wui-convert-input/index.ts | 39 +++++-- .../src/composites/wui-token-button/index.ts | 11 +- .../src/composites/wui-token-button/styles.ts | 1 + 10 files changed, 175 insertions(+), 43 deletions(-) diff --git a/apps/gallery/stories/composites/wui-token-button.stories.ts b/apps/gallery/stories/composites/wui-token-button.stories.ts index 34ca1b4b6c..7e02113fe8 100644 --- a/apps/gallery/stories/composites/wui-token-button.stories.ts +++ b/apps/gallery/stories/composites/wui-token-button.stories.ts @@ -23,5 +23,5 @@ export default { export const Default: Component = { render: args => - html`Recent` + html`Recent` } diff --git a/packages/core/src/controllers/SwapApiController.ts b/packages/core/src/controllers/SwapApiController.ts index b62c9476d7..60a86e55d0 100644 --- a/packages/core/src/controllers/SwapApiController.ts +++ b/packages/core/src/controllers/SwapApiController.ts @@ -228,20 +228,37 @@ export const SwapApiController = { state.swapErrorMessage = undefined }, - async getSwapCalldata() { - const api = this._get1inchApi() - const chainId = CoreHelperUtil.getEvmChainId(NetworkController.state.caipNetwork?.id) + formatNumberToLocalString(number: number) { + return number.toLocaleString('en-US', { + maximumFractionDigits: 2, + minimumFractionDigits: 2 + }) + }, + + getPriceOfTokenAmount(amount: string | undefined, tokenAddress: `0x${string}` | undefined) { + const tokenPrice = state.tokensPriceMap?.[tokenAddress || ''] || '0' + const tokenPriceNumber = parseFloat(tokenPrice) + const amountNumber = amount ? parseFloat(amount) : 0 - const path = OneInchAPIEndpoints.swap(chainId) + if (!tokenPriceNumber) { + return 0 + } + + return this.formatNumberToLocalString(tokenPriceNumber * amountNumber) + }, + + async getSwapCalldata() { + const { api, paths } = this._get1inchApi() const { fromAddress, slippage, sourceTokenAddress, sourceTokenAmount, toTokenAddress } = this._getSwapParams() + console.log('>>> getSwapCalldata') if (!sourceTokenAmount || !state.sourceToken?.address || !state.toToken?.address) { return } // eslint-disable-next-line @typescript-eslint/no-explicit-any const swapTransactionRes = await api.get({ - path, + path: paths.swap, params: { src: sourceTokenAddress, dst: toTokenAddress, @@ -475,6 +492,7 @@ export const SwapApiController = { if (state.sourceToken?.address && state.toToken?.address) { state.loading = true const hasAllowance = await SwapApiController.getTokenAllowance() + console.log('>>> hasAllowance', hasAllowance) if (hasAllowance) { await SwapApiController.getSwapCalldata() diff --git a/packages/scaffold/src/modal/w3m-router/index.ts b/packages/scaffold/src/modal/w3m-router/index.ts index 407f7f5827..d34f590c60 100644 --- a/packages/scaffold/src/modal/w3m-router/index.ts +++ b/packages/scaffold/src/modal/w3m-router/index.ts @@ -124,7 +124,7 @@ export class W3mRouter extends LitElement { case 'ConvertSelectNetwork': return html`` case 'ConvertPreview': - return html`` + return html`` default: return html`` } diff --git a/packages/scaffold/src/views/w3m-convert-preview-view/index.ts b/packages/scaffold/src/views/w3m-convert-preview-view/index.ts index 6dd25d05c0..016ccb3943 100644 --- a/packages/scaffold/src/views/w3m-convert-preview-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-preview-view/index.ts @@ -1,14 +1,39 @@ import { customElement } from '@web3modal/ui' import { LitElement, html } from 'lit' import styles from './styles.js' +import { SwapApiController } from '@web3modal/core' +import { state } from 'lit/decorators.js' @customElement('w3m-convert-preview-view') export class W3mConvertPreviewView extends LitElement { public static override styles = styles + private unsubscribe: ((() => void) | undefined)[] = [] + + // -- State & Properties -------------------------------- // + @state() private sourceTokenAmount = SwapApiController.state.sourceTokenAmount + + @state() private toTokenAmount = SwapApiController.state.toTokenAmount + + @state() private toToken = SwapApiController.state.toToken + + @state() private sourceToken = SwapApiController.state.sourceToken + + @state() private tokensPriceMap = SwapApiController.state.tokensPriceMap + // -- Lifecycle ----------------------------------------- // public constructor() { super() + + this.unsubscribe.push( + ...[ + SwapApiController.subscribe(newState => { + this.sourceToken = newState.sourceToken + this.toToken = newState.toToken + this.tokensPriceMap = newState.tokensPriceMap + }) + ] + ) } // -- Render -------------------------------------------- // @@ -20,10 +45,47 @@ export class W3mConvertPreviewView extends LitElement { // -- Private ------------------------------------------- // private templateSwap() { + const sourceTokenText = `${ + this.sourceTokenAmount + ? SwapApiController.formatNumberToLocalString(parseFloat(this.sourceTokenAmount)) + : '' + } ${this.sourceToken?.symbol}` + const toTokenText = `${ + this.toTokenAmount + ? SwapApiController.formatNumberToLocalString(parseFloat(this.toTokenAmount)) + : '' + } ${this.toToken?.symbol}` + + const sentPrice = SwapApiController.getPriceOfTokenAmount( + this.sourceTokenAmount, + this.sourceToken?.address + ) + const receivePrice = SwapApiController.getPriceOfTokenAmount( + this.toTokenAmount, + this.toToken?.address + ) + return html` - - ${this.templateToken('0.867')} + + + + Send + $${sentPrice} + + + + + - ${this.templateToken('4927.3664')} - $1,718.6 + + + + Receive + $${receivePrice} + + + + @@ -83,26 +161,6 @@ export class W3mConvertPreviewView extends LitElement { ` } - - private templateToken(value: string, networkSrc?: string) { - const networkElement = networkSrc - ? html`` - : html` - - ` - - return html` - - ${value} - ${networkElement} - - ` - } } declare global { diff --git a/packages/scaffold/src/views/w3m-convert-preview-view/styles.ts b/packages/scaffold/src/views/w3m-convert-preview-view/styles.ts index 30fa42fa01..e0c7910beb 100644 --- a/packages/scaffold/src/views/w3m-convert-preview-view/styles.ts +++ b/packages/scaffold/src/views/w3m-convert-preview-view/styles.ts @@ -48,4 +48,8 @@ export default css` border-radius: var(--wui-border-radius-xxs); background: var(--wui-gray-glass-002); } + + .preview-token-details-container { + width: 100%; + } ` diff --git a/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts b/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts index c27c35ac90..3bf232aa3c 100644 --- a/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts @@ -124,7 +124,7 @@ export class W3mConvertSelectTokenView extends LitElement { ${tokens.map( tokenInfo => html` this.onSelectToken(tokenInfo)} > diff --git a/packages/scaffold/src/views/w3m-convert-view/index.ts b/packages/scaffold/src/views/w3m-convert-view/index.ts index 6598d9e10a..5edaea1eac 100644 --- a/packages/scaffold/src/views/w3m-convert-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-view/index.ts @@ -145,16 +145,35 @@ export class W3mConvertView extends LitElement { borderRadius="xs" variant=${!this.hasAllowance || haveNoTokenSelected ? 'shade' : 'fill'} .loading=${this.loadingPrices} - .disabled=${!this.hasAllowance || haveNoTokenSelected} + .disabled=${!this.hasAllowance || haveNoTokenSelected || this.swapErrorMessage} @click=${this.onConvertPreview} > - Swap + ${this.actionButtonLabel()} ` } + private actionButtonLabel(): string { + if (this.swapErrorMessage) { + if (this.swapErrorMessage?.includes('insufficient funds')) { + return 'Insufficient funds' + } + return 'Error' + } + + if (!this.toToken || !this.sourceToken) { + return 'Select token' + } + + if (!this.toTokenAmount || !this.sourceTokenAmount) { + return 'Enter amount' + } + + return this.hasAllowance ? 'Review convert' : 'Not permitted' + } + private templateReplaceTokensButton() { return html`
diff --git a/packages/ui/src/composites/wui-convert-input/index.ts b/packages/ui/src/composites/wui-convert-input/index.ts index 2358db56fd..f4212f2eaa 100644 --- a/packages/ui/src/composites/wui-convert-input/index.ts +++ b/packages/ui/src/composites/wui-convert-input/index.ts @@ -45,9 +45,10 @@ export class WuiConvertInput extends LitElement { this.onFocusChange(true)} @focusout=${() => this.onFocusChange(false)} - .value=${this.value} ?disabled=${this.disabled} - @input=${this.dispatchInputChangeEvent.bind(this)} + .value=${this.value} + @input=${this.dispatchInputChangeEvent} + @keydown=${this.handleKeydown} placeholder="0" /> ${this.value @@ -60,6 +61,35 @@ export class WuiConvertInput extends LitElement { } // -- Private ------------------------------------------- // + private handleKeydown(event: KeyboardEvent) { + const allowedKeys = ['Backspace', 'ArrowLeft', 'ArrowRight', 'Tab'] + const isComma = event.key === ',' + const isDot = event.key === '.' + const isNumericKey = event.key >= '0' && event.key <= '9' + const currentValue = this.value + + if (!isNumericKey && !allowedKeys.includes(event.key) && !isDot && !isComma) { + event.preventDefault() + } + + if (isComma || isDot) { + if (currentValue?.includes('.') || currentValue?.includes(',')) { + event.preventDefault() + } + } + } + + private dispatchInputChangeEvent(event: InputEvent) { + const value = (event.target as HTMLInputElement).value + if (value === ',' || value === '.') { + this.onSetAmount(this.target, '0.') + } else if (value.endsWith(',')) { + this.onSetAmount(this.target, value.replace(',', '.')) + } else { + this.onSetAmount(this.target, value) + } + } + private setMaxValueToInput() { if (this.amount?.toString()) { this.onSetAmount(this.target, this.amount?.toString()) @@ -117,11 +147,6 @@ export class WuiConvertInput extends LitElement { target: this.target }) } - - private dispatchInputChangeEvent(event: Event) { - const input = event.target as HTMLInputElement - this.onSetAmount(this.target, input.value) - } } declare global { diff --git a/packages/ui/src/composites/wui-token-button/index.ts b/packages/ui/src/composites/wui-token-button/index.ts index c88a1387e6..d0881ccda2 100644 --- a/packages/ui/src/composites/wui-token-button/index.ts +++ b/packages/ui/src/composites/wui-token-button/index.ts @@ -4,6 +4,7 @@ import { customElement } from '../../utils/WebComponentsUtil.js' import { resetStyles } from '../../utils/ThemeUtil.js' import styles from './styles.js' import type { TokenInfo } from '@web3modal/core/dist/types/src/controllers/SwapApiController.js' +import type { FlexDirectionType } from '../../utils/TypeUtil.js' @customElement('wui-token-button') export class WuiTokenButton extends LitElement { @@ -13,12 +14,18 @@ export class WuiTokenButton extends LitElement { @property() public logoURI?: string - @property() public symbol?: string + @property() public text?: string + + @property() public flexDirection: FlexDirectionType = 'row' @property() public onClick: (token: TokenInfo) => void = () => {} // -- Render -------------------------------------------- // public override render() { + this.style.cssText = ` + --local-flex-direction: ${this.flexDirection}; + ` + const tokenElement = this.logoURI ? html`` : html` @@ -38,7 +45,7 @@ export class WuiTokenButton extends LitElement { @click=${this.onClick.bind(this)} > ${tokenElement} - ${this.symbol} + ${this.text} ` } diff --git a/packages/ui/src/composites/wui-token-button/styles.ts b/packages/ui/src/composites/wui-token-button/styles.ts index badac76890..0433829103 100644 --- a/packages/ui/src/composites/wui-token-button/styles.ts +++ b/packages/ui/src/composites/wui-token-button/styles.ts @@ -3,6 +3,7 @@ import { css } from 'lit' export default css` :host > button { display: flex; + flex-direction: var(--local-flex-direction); align-items: center; justify-content: center; gap: var(--wui-spacing-xxs); From a93aaf76b7cf5ecb919b1d1d50ba428e6ae96aa4 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Wed, 6 Mar 2024 00:50:50 +0300 Subject: [PATCH 12/96] chore: comment client methods --- packages/ethers5/src/client.ts | 47 ++++++++++++++---------------- packages/wagmi/src/client.ts | 52 ++++++++++++++++++++++++++++++++-- 2 files changed, 71 insertions(+), 28 deletions(-) diff --git a/packages/ethers5/src/client.ts b/packages/ethers5/src/client.ts index 917446659e..b623bf9bd0 100644 --- a/packages/ethers5/src/client.ts +++ b/packages/ethers5/src/client.ts @@ -269,31 +269,28 @@ export class Web3Modal extends Web3ModalScaffold { formatUnits: (value: bigint, decimals: number) => ethers.utils.formatUnits(value, decimals), - sendTransaction: async ({ - address, - chainId, - data, - gasPrice, - to, - value, - gas - }: SendTransactionArgs) => { - const provider = ProviderController.state.provider - const signer = provider?.getSigner() - - const tx = await signer?.sendTransaction({ - chainId, - data, - from: address, - gasPrice, - value, - to, - gasLimit: gas - }) - - await tx?.wait() - - return { hash: tx?.hash } + sendTransaction: async () => { + // const provider = EthersStoreUtil.state.provider + // if (!provider) { + // throw new Error('connectionControllerClient:signMessage - provider is undefined') + // } + + // const tx = await provider.request({ + // method: 'eth_sendTransaction', + // params: { + // chainId, + // data, + // from: address, + // gasPrice, + // value, + // to, + // gasLimit: gas + // } + // }) + + // await tx?() + + return `0x` } } diff --git a/packages/wagmi/src/client.ts b/packages/wagmi/src/client.ts index 337fdd02f8..1bfcac514b 100644 --- a/packages/wagmi/src/client.ts +++ b/packages/wagmi/src/client.ts @@ -9,9 +9,11 @@ import { getAccount, switchChain, watchAccount, - watchConnectors + watchConnectors, + waitForTransactionReceipt } from '@wagmi/core' import { mainnet } from 'viem/chains' +import { prepareTransactionRequest, sendTransaction as wagmiSendTransaction } from '@wagmi/core' import type { Chain } from '@wagmi/core/chains' import type { GetAccountReturnType } from '@wagmi/core' import type { @@ -23,8 +25,10 @@ import type { LibraryOptions, NetworkControllerClient, PublicStateControllerState, + SendTransactionArgs, Token } from '@web3modal/scaffold' +import { formatUnits, parseUnits } from 'viem' import type { Hex } from 'viem' import { Web3ModalScaffold } from '@web3modal/scaffold' import type { Web3ModalSIWEClient } from '@web3modal/siwe' @@ -36,7 +40,7 @@ import { } from './utils/helpers.js' import { W3mFrameHelpers, W3mFrameRpcConstants } from '@web3modal/wallet' import type { W3mFrameProvider } from '@web3modal/wallet' -import { ConstantsUtil as CoreConstants } from '@web3modal/core' +import { ConnectionController, ConstantsUtil as CoreConstants } from '@web3modal/core' import type { defaultWagmiConfig as coreConfig } from './utils/defaultWagmiCoreConfig.js' import type { defaultWagmiConfig as reactConfig } from './utils/defaultWagmiReactConfig.js' @@ -168,7 +172,49 @@ export class Web3Modal extends Web3ModalScaffold { } }, - signMessage: async message => signMessage(this.wagmiConfig, { message }) + signMessage: async message => signMessage(this.wagmiConfig, { message }), + + sendTransaction: async ({ + data, + to, + value, + gas, + gasPrice, + chainId, + address + }: SendTransactionArgs) => { + try { + // Prepare transaction parameters + const params = { + to, + data, + value, + gas, + gasPrice, + chainId, + account: address, + type: 'legacy' + } + + // Prepare the transaction with the given parameters + await prepareTransactionRequest(this.wagmiConfig, params) + + // Send the transaction + const tx = await wagmiSendTransaction(this.wagmiConfig, params) + + // Optionally wait for the transaction to be mined + await waitForTransactionReceipt(this.wagmiConfig, { hash: tx, timeout: 25000 }) + + return tx // Return the transaction object + } catch (error) { + console.error('Transaction failed:', error) + throw error // Rethrow the error to be handled by the caller + } + }, + + parseUnits, + + formatUnits } super({ From ffaa666711d466010129dc0de2fd303d4cfd5fd8 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Wed, 6 Mar 2024 00:51:01 +0300 Subject: [PATCH 13/96] chore: update lock file --- package-lock.json | 729 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 652 insertions(+), 77 deletions(-) diff --git a/package-lock.json b/package-lock.json index ca630ffead..e7573d0bad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -441,6 +441,642 @@ "x-default-browser": "bin/x-default-browser.js" } }, + "node_modules/@aws-crypto/ie11-detection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz", + "integrity": "sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==", + "dev": true, + "dependencies": { + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/ie11-detection/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz", + "integrity": "sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==", + "dev": true, + "dependencies": { + "@aws-crypto/ie11-detection": "^3.0.0", + "@aws-crypto/sha256-js": "^3.0.0", + "@aws-crypto/supports-web-crypto": "^3.0.0", + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz", + "integrity": "sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==", + "dev": true, + "dependencies": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/sha256-js/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz", + "integrity": "sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==", + "dev": true, + "dependencies": { + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/supports-web-crypto/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@aws-crypto/util": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", + "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/util/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@aws-sdk/client-cloudwatch": { + "version": "3.509.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-cloudwatch/-/client-cloudwatch-3.509.0.tgz", + "integrity": "sha512-qWclNb0gOA58DxvYOr+omlkMn8xvg5FoKxvnGUBifJuGf0J2TOv74Y9u2LGm1FCVJskuw1TXnfxzeHpbMJyDyw==", + "dev": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sts": "3.507.0", + "@aws-sdk/core": "3.496.0", + "@aws-sdk/credential-provider-node": "3.509.0", + "@aws-sdk/middleware-host-header": "3.502.0", + "@aws-sdk/middleware-logger": "3.502.0", + "@aws-sdk/middleware-recursion-detection": "3.502.0", + "@aws-sdk/middleware-signing": "3.502.0", + "@aws-sdk/middleware-user-agent": "3.502.0", + "@aws-sdk/region-config-resolver": "3.502.0", + "@aws-sdk/types": "3.502.0", + "@aws-sdk/util-endpoints": "3.502.0", + "@aws-sdk/util-user-agent-browser": "3.502.0", + "@aws-sdk/util-user-agent-node": "3.502.0", + "@smithy/config-resolver": "^2.1.1", + "@smithy/core": "^1.3.1", + "@smithy/fetch-http-handler": "^2.4.1", + "@smithy/hash-node": "^2.1.1", + "@smithy/invalid-dependency": "^2.1.1", + "@smithy/middleware-content-length": "^2.1.1", + "@smithy/middleware-endpoint": "^2.4.1", + "@smithy/middleware-retry": "^2.1.1", + "@smithy/middleware-serde": "^2.1.1", + "@smithy/middleware-stack": "^2.1.1", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/node-http-handler": "^2.3.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/url-parser": "^2.1.1", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-body-length-browser": "^2.1.1", + "@smithy/util-body-length-node": "^2.2.1", + "@smithy/util-defaults-mode-browser": "^2.1.1", + "@smithy/util-defaults-mode-node": "^2.1.1", + "@smithy/util-endpoints": "^1.1.1", + "@smithy/util-retry": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", + "@smithy/util-waiter": "^2.1.1", + "fast-xml-parser": "4.2.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sso": { + "version": "3.507.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.507.0.tgz", + "integrity": "sha512-pFeaKwqv4tXD6QVxWC2V4N62DUoP3bPSm/mCe2SPhaNjNsmwwA53viUHz/nwxIbs8w4vV44UQsygb0AgKm+HoQ==", + "dev": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/core": "3.496.0", + "@aws-sdk/middleware-host-header": "3.502.0", + "@aws-sdk/middleware-logger": "3.502.0", + "@aws-sdk/middleware-recursion-detection": "3.502.0", + "@aws-sdk/middleware-user-agent": "3.502.0", + "@aws-sdk/region-config-resolver": "3.502.0", + "@aws-sdk/types": "3.502.0", + "@aws-sdk/util-endpoints": "3.502.0", + "@aws-sdk/util-user-agent-browser": "3.502.0", + "@aws-sdk/util-user-agent-node": "3.502.0", + "@smithy/config-resolver": "^2.1.1", + "@smithy/core": "^1.3.1", + "@smithy/fetch-http-handler": "^2.4.1", + "@smithy/hash-node": "^2.1.1", + "@smithy/invalid-dependency": "^2.1.1", + "@smithy/middleware-content-length": "^2.1.1", + "@smithy/middleware-endpoint": "^2.4.1", + "@smithy/middleware-retry": "^2.1.1", + "@smithy/middleware-serde": "^2.1.1", + "@smithy/middleware-stack": "^2.1.1", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/node-http-handler": "^2.3.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/url-parser": "^2.1.1", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-body-length-browser": "^2.1.1", + "@smithy/util-body-length-node": "^2.2.1", + "@smithy/util-defaults-mode-browser": "^2.1.1", + "@smithy/util-defaults-mode-node": "^2.1.1", + "@smithy/util-endpoints": "^1.1.1", + "@smithy/util-retry": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.507.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.507.0.tgz", + "integrity": "sha512-ms5CH2ImhqqCIbo5irxayByuPOlVAmSiqDVfjZKwgIziqng2bVgNZMeKcT6t0bmrcgScEAVnZwY7j/iZTIw73g==", + "dev": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sts": "3.507.0", + "@aws-sdk/core": "3.496.0", + "@aws-sdk/middleware-host-header": "3.502.0", + "@aws-sdk/middleware-logger": "3.502.0", + "@aws-sdk/middleware-recursion-detection": "3.502.0", + "@aws-sdk/middleware-signing": "3.502.0", + "@aws-sdk/middleware-user-agent": "3.502.0", + "@aws-sdk/region-config-resolver": "3.502.0", + "@aws-sdk/types": "3.502.0", + "@aws-sdk/util-endpoints": "3.502.0", + "@aws-sdk/util-user-agent-browser": "3.502.0", + "@aws-sdk/util-user-agent-node": "3.502.0", + "@smithy/config-resolver": "^2.1.1", + "@smithy/core": "^1.3.1", + "@smithy/fetch-http-handler": "^2.4.1", + "@smithy/hash-node": "^2.1.1", + "@smithy/invalid-dependency": "^2.1.1", + "@smithy/middleware-content-length": "^2.1.1", + "@smithy/middleware-endpoint": "^2.4.1", + "@smithy/middleware-retry": "^2.1.1", + "@smithy/middleware-serde": "^2.1.1", + "@smithy/middleware-stack": "^2.1.1", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/node-http-handler": "^2.3.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/url-parser": "^2.1.1", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-body-length-browser": "^2.1.1", + "@smithy/util-body-length-node": "^2.2.1", + "@smithy/util-defaults-mode-browser": "^2.1.1", + "@smithy/util-defaults-mode-node": "^2.1.1", + "@smithy/util-endpoints": "^1.1.1", + "@smithy/util-retry": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "@aws-sdk/credential-provider-node": "^3.507.0" + } + }, + "node_modules/@aws-sdk/client-sts": { + "version": "3.507.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.507.0.tgz", + "integrity": "sha512-TOWBe0ApEh32QOib0R+irWGjd1F9wnhbGV5PcB9SakyRwvqwG5MKOfYxG7ocoDqLlaRwzZMidcy/PV8/OEVNKg==", + "dev": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/core": "3.496.0", + "@aws-sdk/middleware-host-header": "3.502.0", + "@aws-sdk/middleware-logger": "3.502.0", + "@aws-sdk/middleware-recursion-detection": "3.502.0", + "@aws-sdk/middleware-user-agent": "3.502.0", + "@aws-sdk/region-config-resolver": "3.502.0", + "@aws-sdk/types": "3.502.0", + "@aws-sdk/util-endpoints": "3.502.0", + "@aws-sdk/util-user-agent-browser": "3.502.0", + "@aws-sdk/util-user-agent-node": "3.502.0", + "@smithy/config-resolver": "^2.1.1", + "@smithy/core": "^1.3.1", + "@smithy/fetch-http-handler": "^2.4.1", + "@smithy/hash-node": "^2.1.1", + "@smithy/invalid-dependency": "^2.1.1", + "@smithy/middleware-content-length": "^2.1.1", + "@smithy/middleware-endpoint": "^2.4.1", + "@smithy/middleware-retry": "^2.1.1", + "@smithy/middleware-serde": "^2.1.1", + "@smithy/middleware-stack": "^2.1.1", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/node-http-handler": "^2.3.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/url-parser": "^2.1.1", + "@smithy/util-base64": "^2.1.1", + "@smithy/util-body-length-browser": "^2.1.1", + "@smithy/util-body-length-node": "^2.2.1", + "@smithy/util-defaults-mode-browser": "^2.1.1", + "@smithy/util-defaults-mode-node": "^2.1.1", + "@smithy/util-endpoints": "^1.1.1", + "@smithy/util-middleware": "^2.1.1", + "@smithy/util-retry": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", + "fast-xml-parser": "4.2.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "@aws-sdk/credential-provider-node": "^3.507.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.496.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.496.0.tgz", + "integrity": "sha512-yT+ug7Cw/3eJi7x2es0+46x12+cIJm5Xv+GPWsrTFD1TKgqO/VPEgfDtHFagDNbFmjNQA65Ygc/kEdIX9ICX/A==", + "dev": true, + "dependencies": { + "@smithy/core": "^1.3.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/signature-v4": "^2.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.502.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.502.0.tgz", + "integrity": "sha512-KIB8Ae1Z7domMU/jU4KiIgK4tmYgvuXlhR54ehwlVHxnEoFPoPuGHFZU7oFn79jhhSLUFQ1lRYMxP0cEwb7XeQ==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.502.0", + "@smithy/property-provider": "^2.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.503.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.503.1.tgz", + "integrity": "sha512-rTdlFFGoPPFMF2YjtlfRuSgKI+XsF49u7d98255hySwhsbwd3Xp+utTTPquxP+CwDxMHbDlI7NxDzFiFdsoZug==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.502.0", + "@smithy/fetch-http-handler": "^2.4.1", + "@smithy/node-http-handler": "^2.3.1", + "@smithy/property-provider": "^2.1.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/smithy-client": "^2.3.1", + "@smithy/types": "^2.9.1", + "@smithy/util-stream": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.507.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.507.0.tgz", + "integrity": "sha512-2CnyduoR9COgd7qH1LPYK8UggGqVs8R4ASDMB5bwGxbg9ZerlStDiHpqvJNNg1k+VlejBr++utxfmHd236XgmQ==", + "dev": true, + "dependencies": { + "@aws-sdk/client-sts": "3.507.0", + "@aws-sdk/credential-provider-env": "3.502.0", + "@aws-sdk/credential-provider-process": "3.502.0", + "@aws-sdk/credential-provider-sso": "3.507.0", + "@aws-sdk/credential-provider-web-identity": "3.507.0", + "@aws-sdk/types": "3.502.0", + "@smithy/credential-provider-imds": "^2.2.1", + "@smithy/property-provider": "^2.1.1", + "@smithy/shared-ini-file-loader": "^2.3.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.509.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.509.0.tgz", + "integrity": "sha512-uXT8wIq1k+m0mS/pC9U1cUTIjUB7/4PgxyiYsTxYPIULtWnQXltAlcPU3QzKTJMP60sqftRYZ2jFDLAVsipQxw==", + "dev": true, + "dependencies": { + "@aws-sdk/credential-provider-env": "3.502.0", + "@aws-sdk/credential-provider-http": "3.503.1", + "@aws-sdk/credential-provider-ini": "3.507.0", + "@aws-sdk/credential-provider-process": "3.502.0", + "@aws-sdk/credential-provider-sso": "3.507.0", + "@aws-sdk/credential-provider-web-identity": "3.507.0", + "@aws-sdk/types": "3.502.0", + "@smithy/credential-provider-imds": "^2.2.1", + "@smithy/property-provider": "^2.1.1", + "@smithy/shared-ini-file-loader": "^2.3.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.502.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.502.0.tgz", + "integrity": "sha512-fJJowOjQ4infYQX0E1J3xFVlmuwEYJAFk0Mo1qwafWmEthsBJs+6BR2RiWDELHKrSK35u4Pf3fu3RkYuCtmQFw==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.502.0", + "@smithy/property-provider": "^2.1.1", + "@smithy/shared-ini-file-loader": "^2.3.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.507.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.507.0.tgz", + "integrity": "sha512-6WBjou52QukFpDi4ezb19bcAx/bM8ge8qnJnRT02WVRmU6zFQ5yLD2fW1MFsbX3cwbey+wSqKd5FGE1Hukd5wQ==", + "dev": true, + "dependencies": { + "@aws-sdk/client-sso": "3.507.0", + "@aws-sdk/token-providers": "3.507.0", + "@aws-sdk/types": "3.502.0", + "@smithy/property-provider": "^2.1.1", + "@smithy/shared-ini-file-loader": "^2.3.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.507.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.507.0.tgz", + "integrity": "sha512-f+aGMfazBimX7S06224JRYzGTaMh1uIhfj23tZylPJ05KxTVi5IO1RoqeI/uHLJ+bDOx+JHBC04g/oCdO4kHvw==", + "dev": true, + "dependencies": { + "@aws-sdk/client-sts": "3.507.0", + "@aws-sdk/types": "3.502.0", + "@smithy/property-provider": "^2.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.502.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.502.0.tgz", + "integrity": "sha512-EjnG0GTYXT/wJBmm5/mTjDcAkzU8L7wQjOzd3FTXuTCNNyvAvwrszbOj5FlarEw5XJBbQiZtBs+I5u9+zy560w==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.502.0", + "@smithy/protocol-http": "^3.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.502.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.502.0.tgz", + "integrity": "sha512-FDyv6K4nCoHxbjLGS2H8ex8I0KDIiu4FJgVRPs140ZJy6gE5Pwxzv6YTzZGLMrnqcIs9gh065Lf6DjwMelZqaw==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.502.0", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.502.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.502.0.tgz", + "integrity": "sha512-hvbyGJbxeuezxOu8VfFmcV4ql1hKXLxHTe5FNYfEBat2KaZXVhc1Hg+4TvB06/53p+E8J99Afmumkqbxs2esUA==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.502.0", + "@smithy/protocol-http": "^3.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-signing": { + "version": "3.502.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.502.0.tgz", + "integrity": "sha512-4hF08vSzJ7L6sB+393gOFj3s2N6nLusYS0XrMW6wYNFU10IDdbf8Z3TZ7gysDJJHEGQPmTAesPEDBsasGWcMxg==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.502.0", + "@smithy/property-provider": "^2.1.1", + "@smithy/protocol-http": "^3.1.1", + "@smithy/signature-v4": "^2.1.1", + "@smithy/types": "^2.9.1", + "@smithy/util-middleware": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.502.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.502.0.tgz", + "integrity": "sha512-TxbBZbRiXPH0AUxegqiNd9aM9zNSbfjtBs5MEfcBsweeT/B2O7K1EjP9+CkB8Xmk/5FLKhAKLr19b1TNoE27rw==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.502.0", + "@aws-sdk/util-endpoints": "3.502.0", + "@smithy/protocol-http": "^3.1.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.502.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.502.0.tgz", + "integrity": "sha512-mxmsX2AGgnSM+Sah7mcQCIneOsJQNiLX0COwEttuf8eO+6cLMAZvVudH3BnWTfea4/A9nuri9DLCqBvEmPrilg==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.502.0", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/types": "^2.9.1", + "@smithy/util-config-provider": "^2.2.1", + "@smithy/util-middleware": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.507.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.507.0.tgz", + "integrity": "sha512-ehOINGjoGJc6Puzon7ev4bXckkaZx18WNgMTNttYJhj3vTpj5LPSQbI/5SS927bEbpGMFz1+hJ6Ra5WGfbTcEQ==", + "dev": true, + "dependencies": { + "@aws-sdk/client-sso-oidc": "3.507.0", + "@aws-sdk/types": "3.502.0", + "@smithy/property-provider": "^2.1.1", + "@smithy/shared-ini-file-loader": "^2.3.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.502.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.502.0.tgz", + "integrity": "sha512-M0DSPYe/gXhwD2QHgoukaZv5oDxhW3FfvYIrJptyqUq3OnPJBcDbihHjrE0PBtfh/9kgMZT60/fQ2NVFANfa2g==", + "dev": true, + "dependencies": { + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.502.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.502.0.tgz", + "integrity": "sha512-6LKFlJPp2J24r1Kpfoz5ESQn+1v5fEjDB3mtUKRdpwarhm3syu7HbKlHCF3KbcCOyahobvLvhoedT78rJFEeeg==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.502.0", + "@smithy/types": "^2.9.1", + "@smithy/util-endpoints": "^1.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.495.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.495.0.tgz", + "integrity": "sha512-MfaPXT0kLX2tQaR90saBT9fWQq2DHqSSJRzW+MZWsmF+y5LGCOhO22ac/2o6TKSQm7h0HRc2GaADqYYYor62yg==", + "dev": true, + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.502.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.502.0.tgz", + "integrity": "sha512-v8gKyCs2obXoIkLETAeEQ3AM+QmhHhst9xbM1cJtKUGsRlVIak/XyyD+kVE6kmMm1cjfudHpHKABWk9apQcIZQ==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.502.0", + "@smithy/types": "^2.9.1", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.502.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.502.0.tgz", + "integrity": "sha512-9RjxpkGZKbTdl96tIJvAo+vZoz4P/cQh36SBUt9xfRfW0BtsaLyvSrvlR5wyUYhvRcC12Axqh/8JtnAPq//+Vw==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.502.0", + "@smithy/node-config-provider": "^2.2.1", + "@smithy/types": "^2.9.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/util-utf8-browser": { + "version": "3.259.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", + "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", + "dev": true, + "dependencies": { + "tslib": "^2.3.1" + } + }, "node_modules/@babel/code-frame": { "version": "7.23.4", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.4.tgz", @@ -12195,22 +12831,6 @@ "@types/responselike": "^1.0.0" } }, - "node_modules/@types/chai": { - "version": "4.3.11", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.11.tgz", - "integrity": "sha512-qQR1dr2rGIHYlJulmr8Ioq3De0Le9E4MJ5AiaeAETJJpndT1uUNHsGFK3L/UIu+rbkQSdj8J/w2bCsBZc/Y5fQ==", - "dev": true - }, - "node_modules/@types/chai-subset": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/chai-subset/-/chai-subset-1.3.5.tgz", - "integrity": "sha512-c2mPnw+xHtXDoHmdtcCXGwyLMiauiAyxWMzhGpqHC4nqI/Y5G2XhTampslK2rb59kpcuHon03UH8W6iYUzw88A==", - "dev": true, - "dependencies": { - "@types/filesystem": "*", - "@types/har-format": "*" - } - }, "node_modules/@types/connect": { "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", @@ -12289,19 +12909,6 @@ "@types/send": "*" } }, - "node_modules/@types/filesystem": { - "version": "0.0.35", - "resolved": "https://registry.npmjs.org/@types/filesystem/-/filesystem-0.0.35.tgz", - "integrity": "sha512-1eKvCaIBdrD2mmMgy5dwh564rVvfEhZTWVQQGRNn0Nt4ZEnJ0C8oSUCzvMKRA4lGde5oEVo+q2MrTTbV/GHDCQ==", - "dependencies": { - "@types/filewriter": "*" - } - }, - "node_modules/@types/filewriter": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/@types/filewriter/-/filewriter-0.0.33.tgz", - "integrity": "sha512-xFU8ZXTw4gd358lb2jw25nxY9QAgqn2+bKKjKOYfNCzN4DKCFetK7sPtrlpg66Ywe3vWY9FNxprZawAh9wfJ3g==" - }, "node_modules/@types/find-cache-dir": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/@types/find-cache-dir/-/find-cache-dir-3.2.1.tgz", @@ -12326,11 +12933,6 @@ "@types/node": "*" } }, - "node_modules/@types/har-format": { - "version": "1.2.15", - "resolved": "https://registry.npmjs.org/@types/har-format/-/har-format-1.2.15.tgz", - "integrity": "sha512-RpQH4rXLuvTXKR0zqHq3go0RVXYv/YVqv4TnPH95VbwUxZdQlK1EtcMvQvMpDngHbt13Csh9Z4qT9AbkiQH5BA==" - }, "node_modules/@types/http-cache-semantics": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", @@ -12434,9 +13036,9 @@ "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" }, "node_modules/@types/node": { - "version": "20.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.0.tgz", - "integrity": "sha512-D0WfRmU9TQ8I9PFx9Yc+EBHw+vSpIub4IDvQivcp26PtPrdMGAq5SDcpXEo/epqa/DXotVpekHiLNTg3iaKXBQ==", + "version": "20.11.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.5.tgz", + "integrity": "sha512-g557vgQjUUfN76MZAN/dt1z3dzcUsimuysco0KeluHgrPdJXkP/XdAURgyO2W9fZWHRtRBiVKzKn8vyOAwlG+w==", "dependencies": { "undici-types": "~5.26.4" } @@ -12593,7 +13195,6 @@ "version": "17.0.32", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", - "dev": true, "dependencies": { "@types/yargs-parser": "*" } @@ -14484,19 +15085,11 @@ "node": ">=12.0.0" } }, - "node_modules/big-integer": { - "version": "1.6.52", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", - "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/bigint-buffer": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/bigint-buffer/-/bigint-buffer-1.1.5.tgz", - "integrity": "sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA==", - "hasInstallScript": true, + "node_modules/better-path-resolve": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/better-path-resolve/-/better-path-resolve-1.0.0.tgz", + "integrity": "sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==", + "dev": true, "dependencies": { "is-windows": "^1.0.0" }, @@ -25636,7 +26229,7 @@ "version": "3.29.4", "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", - "devOptional": true, + "dev": true, "bin": { "rollup": "dist/bin/rollup" }, @@ -25648,35 +26241,11 @@ "fsevents": "~2.3.2" } }, - "node_modules/rpc-websockets": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/rpc-websockets/-/rpc-websockets-7.8.0.tgz", - "integrity": "sha512-AStkq6KDvSAmA4WiwlK1pDvj/33BWmExTATUokC0v+NhWekXSTNzXS5OGXeYwq501/pj6lBZMofg/h4dx4/tCg==", - "dependencies": { - "open": "^8.4.0", - "picomatch": "^2.3.1", - "source-map": "^0.7.4", - "yargs": "^17.5.1" - }, - "bin": { - "rollup-plugin-visualizer": "dist/bin/cli.js" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "rollup": "2.x || 3.x || 4.x" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, "node_modules/rollup-plugin-visualizer/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "extraneous": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -25691,6 +26260,7 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "extraneous": true, "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", @@ -25704,6 +26274,7 @@ "version": "0.7.4", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "extraneous": true, "engines": { "node": ">= 8" } @@ -25712,6 +26283,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "extraneous": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -25728,6 +26300,7 @@ "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "extraneous": true, "engines": { "node": ">=10" } @@ -25736,6 +26309,7 @@ "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "extraneous": true, "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -25753,6 +26327,7 @@ "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "extraneous": true, "engines": { "node": ">=12" } From bf39179ee072a2999f6099ae929fed2eff4edccc Mon Sep 17 00:00:00 2001 From: enesozturk Date: Thu, 7 Mar 2024 00:09:21 +0300 Subject: [PATCH 14/96] refactor: ui changes, add gas price API --- .../core/src/controllers/SwapApiController.ts | 38 ++++++++- .../views/w3m-convert-preview-view/index.ts | 84 +++++++++++++++---- .../views/w3m-convert-preview-view/styles.ts | 42 +++++++++- .../w3m-convert-select-token-view/index.ts | 8 +- .../src/views/w3m-convert-view/index.ts | 8 +- packages/wagmi/src/client.ts | 42 ++++------ 6 files changed, 170 insertions(+), 52 deletions(-) diff --git a/packages/core/src/controllers/SwapApiController.ts b/packages/core/src/controllers/SwapApiController.ts index 60a86e55d0..23ad833acc 100644 --- a/packages/core/src/controllers/SwapApiController.ts +++ b/packages/core/src/controllers/SwapApiController.ts @@ -8,10 +8,12 @@ import { ConstantsUtil } from '../utils/ConstantsUtil.js' import { ConnectionController } from './ConnectionController.js' const ONEINCH_API_BASE_URL = 'https://1inch-swap-proxy.walletconnect-v1-bridge.workers.dev' +const CURRENT_CHAIN_ADDRESS = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee' const OneInchAPIEndpoints = { approveTransaction: (chainId: number) => `/swap/v5.2/${chainId}/approve/transaction`, approveAllowance: (chainId: number) => `/swap/v5.2/${chainId}/approve/allowance`, + gasPrice: (chainId: number) => `/gas-price/v1.5/${chainId}`, swap: (chainId: number) => `/swap/v5.2/${chainId}/swap`, tokens: (chainId: number) => `/swap/v5.2/${chainId}/tokens`, tokensCustom: (chainId: number) => `/token/v1.2/${chainId}/custom`, @@ -37,6 +39,7 @@ export interface SwapApiControllerState { hasAllowance: boolean loading?: boolean tokens?: Record + popularTokens?: Record foundTokens?: TokenInfo[] myTokensWithBalance?: Record tokensPriceMap: Record @@ -116,12 +119,14 @@ const state = proxy({ initialLoading: false, loading: false, tokens: undefined, + popularTokens: undefined, foundTokens: undefined, myTokensWithBalance: undefined, tokensPriceMap: {}, swapErrorMessage: undefined, isTransactionPending: false, - loadingPrices: false + loadingPrices: false, + swapTransaction: undefined }) // -- Controller ---------------------------------------- // @@ -146,6 +151,7 @@ export const SwapApiController = { paths: { approveTransaction: OneInchAPIEndpoints.approveTransaction(chainId), approveAllowance: OneInchAPIEndpoints.approveAllowance(chainId), + gasPrice: OneInchAPIEndpoints.gasPrice(chainId), swap: OneInchAPIEndpoints.swap(chainId), tokens: OneInchAPIEndpoints.tokens(chainId), tokensCustom: OneInchAPIEndpoints.tokensCustom(chainId), @@ -247,6 +253,33 @@ export const SwapApiController = { return this.formatNumberToLocalString(tokenPriceNumber * amountNumber) }, + async getGasPrice() { + const { api, paths } = this._get1inchApi() + + const gasPrices = await api.get>({ + path: paths.gasPrice, + headers: { 'content-type': 'application/json' } + }) + + return gasPrices + }, + + async getCurrentNetworkTokenPrice() { + const { api, paths } = this._get1inchApi() + const prices = await api.post>({ + path: paths.tokenPrices, + body: { tokens: [CURRENT_CHAIN_ADDRESS], currency: 'USD' }, + headers: { + 'content-type': 'application/json' + } + }) + + const priceString = prices?.[CURRENT_CHAIN_ADDRESS] || '0' + const price = parseFloat(priceString) + + return price + }, + async getSwapCalldata() { const { api, paths } = this._get1inchApi() const { fromAddress, slippage, sourceTokenAddress, sourceTokenAmount, toTokenAddress } = @@ -345,7 +378,8 @@ export const SwapApiController = { const res = await api.get({ path: paths.tokens }) - state.tokens = Object.entries(res.tokens) + state.tokens = res.tokens + state.popularTokens = Object.entries(res.tokens) .sort(([, aTokenInfo], [, bTokenInfo]) => { if (aTokenInfo.symbol < bTokenInfo.symbol) { return -1 diff --git a/packages/scaffold/src/views/w3m-convert-preview-view/index.ts b/packages/scaffold/src/views/w3m-convert-preview-view/index.ts index 016ccb3943..fe444e3ba2 100644 --- a/packages/scaffold/src/views/w3m-convert-preview-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-preview-view/index.ts @@ -1,8 +1,15 @@ import { customElement } from '@web3modal/ui' import { LitElement, html } from 'lit' import styles from './styles.js' -import { SwapApiController } from '@web3modal/core' +import { + AccountController, + AssetUtil, + NetworkController, + RouterController, + SwapApiController +} from '@web3modal/core' import { state } from 'lit/decorators.js' +import { ifDefined } from 'lit/directives/if-defined.js' @customElement('w3m-convert-preview-view') export class W3mConvertPreviewView extends LitElement { @@ -19,7 +26,11 @@ export class W3mConvertPreviewView extends LitElement { @state() private sourceToken = SwapApiController.state.sourceToken - @state() private tokensPriceMap = SwapApiController.state.tokensPriceMap + @state() private caipNetwork = NetworkController.state.caipNetwork + + @state() private isTransactionPending = SwapApiController.state.isTransactionPending + + @state() private balanceSymbol = AccountController.state.balanceSymbol // -- Lifecycle ----------------------------------------- // public constructor() { @@ -27,10 +38,23 @@ export class W3mConvertPreviewView extends LitElement { this.unsubscribe.push( ...[ + AccountController.subscribeKey('balanceSymbol', newBalanceSymbol => { + if (this.balanceSymbol !== newBalanceSymbol) { + RouterController.goBack() + // TODO(enes): maybe reset state as well? + } + }), + NetworkController.subscribeKey('caipNetwork', newCaipNetwork => { + if (this.caipNetwork !== newCaipNetwork) { + this.caipNetwork = newCaipNetwork + } + }), SwapApiController.subscribe(newState => { this.sourceToken = newState.sourceToken this.toToken = newState.toToken - this.tokensPriceMap = newState.tokensPriceMap + this.sourceTokenAmount = newState.sourceTokenAmount + this.toTokenAmount = newState.toTokenAmount + this.isTransactionPending = newState.isTransactionPending }) ] ) @@ -122,20 +146,17 @@ export class W3mConvertPreviewView extends LitElement { - Price - - 1 ETH = 5,700.05 1INCH - $2,003.62 - + Network cost + $- Network - Ethereum + ${this.caipNetwork?.name} @@ -149,18 +170,53 @@ export class W3mConvertPreviewView extends LitElement { - Service fee - - Free + Provider fee + + Free - + + + Review transaction carefully + + + + + ` } + + private onCancelTransaction() { + RouterController.goBack() + } + + private async onSendTransaction() { + await SwapApiController.swapTokens() + } } declare global { diff --git a/packages/scaffold/src/views/w3m-convert-preview-view/styles.ts b/packages/scaffold/src/views/w3m-convert-preview-view/styles.ts index e0c7910beb..88866eff9c 100644 --- a/packages/scaffold/src/views/w3m-convert-preview-view/styles.ts +++ b/packages/scaffold/src/views/w3m-convert-preview-view/styles.ts @@ -42,6 +42,10 @@ export default css` background: var(--wui-gray-glass-005); } + .preview-token-details-container { + width: 100%; + } + .details-row { width: 100%; padding: var(--wui-spacing-s) var(--wui-spacing-xl); @@ -49,7 +53,43 @@ export default css` background: var(--wui-gray-glass-002); } - .preview-token-details-container { + .action-buttons-container { width: 100%; + gap: var(--wui-spacing-xs); + } + + .action-buttons-container > button { + display: flex; + align-items: center; + justify-content: center; + background: transparent; + height: 48px; + border-radius: var(--wui-border-radius-xs); + border: none; + box-shadow: inset 0 0 0 1px var(--wui-gray-glass-010); + } + + .action-buttons-container > button:disabled { + opacity: 0.8; + cursor: not-allowed; + } + + .action-buttons-container > button.cancel-button { + flex: 2; + } + + .action-buttons-container > button.convert-button { + flex: 4; + background-color: var(--wui-color-accent-090); + } + + .action-buttons-container > button.convert-button > wui-text { + color: white; + } + + .free-badge { + background: rgba(38, 217, 98, 0.15); + border-radius: var(--wui-border-radius-4xs); + padding: 4.5px 6px; } ` diff --git a/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts b/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts index 3bf232aa3c..c4ae638cc9 100644 --- a/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts @@ -92,8 +92,8 @@ export class W3mConvertSelectTokenView extends LitElement { Popular tokens - ${SwapApiController.state.tokens && - Object.values(SwapApiController.state.tokens).map( + ${SwapApiController.state.popularTokens && + Object.values(SwapApiController.state.popularTokens).map( tokenInfo => html` { - console.log('>>> newState', newState.tokensPriceMap) if (this.loading !== newState.loading) { this.loading = newState.loading } @@ -108,8 +107,6 @@ export class W3mConvertView extends LitElement { // -- Render -------------------------------------------- // public override render() { - console.log('>>> toToken', this.toToken) - return html` ${this.initialLoading || this.isTransactionPending @@ -291,10 +288,7 @@ export class W3mConvertView extends LitElement { Network cost - - $5.3836 - 15.4007 1INCH - + $- diff --git a/packages/wagmi/src/client.ts b/packages/wagmi/src/client.ts index 1bfcac514b..5b09faa9b9 100644 --- a/packages/wagmi/src/client.ts +++ b/packages/wagmi/src/client.ts @@ -40,7 +40,7 @@ import { } from './utils/helpers.js' import { W3mFrameHelpers, W3mFrameRpcConstants } from '@web3modal/wallet' import type { W3mFrameProvider } from '@web3modal/wallet' -import { ConnectionController, ConstantsUtil as CoreConstants } from '@web3modal/core' +import { ConstantsUtil as CoreConstants } from '@web3modal/core' import type { defaultWagmiConfig as coreConfig } from './utils/defaultWagmiCoreConfig.js' import type { defaultWagmiConfig as reactConfig } from './utils/defaultWagmiReactConfig.js' @@ -174,33 +174,27 @@ export class Web3Modal extends Web3ModalScaffold { signMessage: async message => signMessage(this.wagmiConfig, { message }), - sendTransaction: async ({ - data, - to, - value, - gas, - gasPrice, - chainId, - address - }: SendTransactionArgs) => { + sendTransaction: async (data: SendTransactionArgs) => { try { - // Prepare transaction parameters - const params = { - to, - data, - value, - gas, - gasPrice, - chainId, - account: address, - type: 'legacy' - } - // Prepare the transaction with the given parameters - await prepareTransactionRequest(this.wagmiConfig, params) + await prepareTransactionRequest(this.wagmiConfig, { + account: data.address, + to: data.to, + value: data.value, + gas: data.gas, + gasPrice: data.gasPrice, + type: 'legacy' + }) // Send the transaction - const tx = await wagmiSendTransaction(this.wagmiConfig, params) + const tx = await wagmiSendTransaction(this.wagmiConfig, { + account: data.address, + to: data.to, + value: data.value, + gas: data.gas, + gasPrice: data.gasPrice, + type: 'legacy' + }) // Optionally wait for the transaction to be mined await waitForTransactionReceipt(this.wagmiConfig, { hash: tx, timeout: 25000 }) From 7c4a76f8bcd394a403a21344727caf1543528b3e Mon Sep 17 00:00:00 2001 From: enesozturk Date: Thu, 7 Mar 2024 00:26:34 +0300 Subject: [PATCH 15/96] fix: build issues --- .../tests/controllers/ConnectionController.test.ts | 4 ++-- packages/ethers5/src/client.ts | 1 - .../views/w3m-convert-select-network-view/index.ts | 7 ++----- .../ui/src/composites/wui-convert-input/index.ts | 13 ++++++++++++- .../ui/src/composites/wui-token-button/index.ts | 13 ++++++++++++- 5 files changed, 28 insertions(+), 10 deletions(-) diff --git a/packages/core/tests/controllers/ConnectionController.test.ts b/packages/core/tests/controllers/ConnectionController.test.ts index 23e14c54a7..a90ebc0c2a 100644 --- a/packages/core/tests/controllers/ConnectionController.test.ts +++ b/packages/core/tests/controllers/ConnectionController.test.ts @@ -18,7 +18,7 @@ const client: ConnectionControllerClient = { checkInstalled: _id => true, parseUnits: value => BigInt(value), formatUnits: value => value.toString(), - sendTransaction: () => Promise.resolve({ hash: '0x' }) + sendTransaction: () => Promise.resolve('0x') } const partialClient: ConnectionControllerClient = { @@ -27,7 +27,7 @@ const partialClient: ConnectionControllerClient = { signMessage: async (message: string) => Promise.resolve(message), parseUnits: value => BigInt(value), formatUnits: value => value.toString(), - sendTransaction: () => Promise.resolve({ hash: '0x' }) + sendTransaction: () => Promise.resolve('0x') } // -- Tests -------------------------------------------------------------------- diff --git a/packages/ethers5/src/client.ts b/packages/ethers5/src/client.ts index b623bf9bd0..63e8cf1d64 100644 --- a/packages/ethers5/src/client.ts +++ b/packages/ethers5/src/client.ts @@ -8,7 +8,6 @@ import type { LibraryOptions, NetworkControllerClient, PublicStateControllerState, - SendTransactionArgs, Token } from '@web3modal/scaffold' import { Web3ModalScaffold } from '@web3modal/scaffold' diff --git a/packages/scaffold/src/views/w3m-convert-select-network-view/index.ts b/packages/scaffold/src/views/w3m-convert-select-network-view/index.ts index 5c8f0fc50d..2652361f54 100644 --- a/packages/scaffold/src/views/w3m-convert-select-network-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-select-network-view/index.ts @@ -2,7 +2,7 @@ import { customElement } from '@web3modal/ui' import { LitElement, html } from 'lit' import styles from './styles.js' import { state } from 'lit/decorators.js' -import { AccountController, NetworkController } from '@web3modal/core' +import { NetworkController } from '@web3modal/core' const yourItems = [ { @@ -40,10 +40,7 @@ export class W3mConvertSelectNetworkView extends LitElement { public constructor() { super() this.unsubscribe.push( - ...[ - NetworkController.subscribeKey('caipNetwork', val => (this.network = val)), - AccountController.subscribeKey('isConnected', val => (this.connected = val)) - ] + ...[NetworkController.subscribeKey('caipNetwork', val => (this.network = val))] ) } diff --git a/packages/ui/src/composites/wui-convert-input/index.ts b/packages/ui/src/composites/wui-convert-input/index.ts index f4212f2eaa..845135fefe 100644 --- a/packages/ui/src/composites/wui-convert-input/index.ts +++ b/packages/ui/src/composites/wui-convert-input/index.ts @@ -6,10 +6,21 @@ import '../../components/wui-text/index.js' import '../wui-transaction-visual/index.js' import { EventsController, RouterController } from '@web3modal/core' import styles from './styles.js' -import type { TokenInfo } from '@web3modal/core/dist/types/src/controllers/SwapApiController.js' type Target = 'sourceToken' | 'toToken' +interface TokenInfo { + address: `0x${string}` + symbol: string + name: string + decimals: number + logoURI: string + domainVersion?: string + eip2612?: boolean + isFoT?: boolean + tags?: string[] +} + @customElement('wui-convert-input') export class WuiConvertInput extends LitElement { public static override styles = [resetStyles, styles] diff --git a/packages/ui/src/composites/wui-token-button/index.ts b/packages/ui/src/composites/wui-token-button/index.ts index d0881ccda2..ae6d0d5e3a 100644 --- a/packages/ui/src/composites/wui-token-button/index.ts +++ b/packages/ui/src/composites/wui-token-button/index.ts @@ -3,9 +3,20 @@ import { property } from 'lit/decorators.js' import { customElement } from '../../utils/WebComponentsUtil.js' import { resetStyles } from '../../utils/ThemeUtil.js' import styles from './styles.js' -import type { TokenInfo } from '@web3modal/core/dist/types/src/controllers/SwapApiController.js' import type { FlexDirectionType } from '../../utils/TypeUtil.js' +interface TokenInfo { + address: `0x${string}` + symbol: string + name: string + decimals: number + logoURI: string + domainVersion?: string + eip2612?: boolean + isFoT?: boolean + tags?: string[] +} + @customElement('wui-token-button') export class WuiTokenButton extends LitElement { public static override styles = [resetStyles, styles] From 8781d6bad3b7954aa55b3d094c278a22c940e17e Mon Sep 17 00:00:00 2001 From: enesozturk Date: Thu, 7 Mar 2024 00:45:32 +0300 Subject: [PATCH 16/96] fix: linter issues --- .../core/src/controllers/SwapApiController.ts | 14 +++------- packages/ethers5/src/client.ts | 26 ++++--------------- .../views/w3m-convert-preview-view/index.ts | 2 +- .../w3m-convert-select-network-view/index.ts | 9 +------ .../w3m-convert-select-token-view/index.ts | 14 ++++++++-- .../src/views/w3m-convert-view/index.ts | 1 + .../src/composites/wui-convert-input/index.ts | 6 ++++- .../src/composites/wui-token-button/index.ts | 4 +-- packages/wagmi/src/client.ts | 9 +++---- 9 files changed, 34 insertions(+), 51 deletions(-) diff --git a/packages/core/src/controllers/SwapApiController.ts b/packages/core/src/controllers/SwapApiController.ts index 23ad833acc..c92964129f 100644 --- a/packages/core/src/controllers/SwapApiController.ts +++ b/packages/core/src/controllers/SwapApiController.ts @@ -285,7 +285,6 @@ export const SwapApiController = { const { fromAddress, slippage, sourceTokenAddress, sourceTokenAmount, toTokenAddress } = this._getSwapParams() - console.log('>>> getSwapCalldata') if (!sourceTokenAmount || !state.sourceToken?.address || !state.toToken?.address) { return } @@ -434,11 +433,7 @@ export const SwapApiController = { this.getTokenPriceWithAddresses(tokenAddresses) ]) - const mergedTokensWithBalances = await this.mergeTokenWithBalances( - tokens, - balances, - tokensPrice - ) + const mergedTokensWithBalances = this.mergeTokenWithBalances(tokens, balances, tokensPrice) state.myTokensWithBalance = mergedTokensWithBalances @@ -468,7 +463,7 @@ export const SwapApiController = { return { balances: nonEmptyBalances, tokenAddresses: Object.keys(nonEmptyBalances) } }, - async getTokenInfoWithAddresses(addresses: Array) { + async getTokenInfoWithAddresses(addresses: string[]) { const { api, paths } = this._get1inchApi() return api.get>({ @@ -477,7 +472,7 @@ export const SwapApiController = { }) }, - async getTokenPriceWithAddresses(addresses: Array) { + async getTokenPriceWithAddresses(addresses: string[]) { state.loadingPrices = true const { api, paths } = this._get1inchApi() @@ -499,7 +494,7 @@ export const SwapApiController = { return prices }, - async mergeTokenWithBalances( + mergeTokenWithBalances( tokens: Record, balances: Record, tokensPrice: Record @@ -526,7 +521,6 @@ export const SwapApiController = { if (state.sourceToken?.address && state.toToken?.address) { state.loading = true const hasAllowance = await SwapApiController.getTokenAllowance() - console.log('>>> hasAllowance', hasAllowance) if (hasAllowance) { await SwapApiController.getSwapCalldata() diff --git a/packages/ethers5/src/client.ts b/packages/ethers5/src/client.ts index 63e8cf1d64..01510a8ee5 100644 --- a/packages/ethers5/src/client.ts +++ b/packages/ethers5/src/client.ts @@ -269,27 +269,11 @@ export class Web3Modal extends Web3ModalScaffold { formatUnits: (value: bigint, decimals: number) => ethers.utils.formatUnits(value, decimals), sendTransaction: async () => { - // const provider = EthersStoreUtil.state.provider - // if (!provider) { - // throw new Error('connectionControllerClient:signMessage - provider is undefined') - // } - - // const tx = await provider.request({ - // method: 'eth_sendTransaction', - // params: { - // chainId, - // data, - // from: address, - // gasPrice, - // value, - // to, - // gasLimit: gas - // } - // }) - - // await tx?() - - return `0x` + // Mock await expression, will be implemented + // eslint-disable-next-line no-promise-executor-return + await new Promise(resolve => setTimeout(resolve, 0)) + + return '0x' } } diff --git a/packages/scaffold/src/views/w3m-convert-preview-view/index.ts b/packages/scaffold/src/views/w3m-convert-preview-view/index.ts index fe444e3ba2..ca4566c44f 100644 --- a/packages/scaffold/src/views/w3m-convert-preview-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-preview-view/index.ts @@ -41,7 +41,7 @@ export class W3mConvertPreviewView extends LitElement { AccountController.subscribeKey('balanceSymbol', newBalanceSymbol => { if (this.balanceSymbol !== newBalanceSymbol) { RouterController.goBack() - // TODO(enes): maybe reset state as well? + // Maybe reset state as well? } }), NetworkController.subscribeKey('caipNetwork', newCaipNetwork => { diff --git a/packages/scaffold/src/views/w3m-convert-select-network-view/index.ts b/packages/scaffold/src/views/w3m-convert-select-network-view/index.ts index 2652361f54..60bf156ba0 100644 --- a/packages/scaffold/src/views/w3m-convert-select-network-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-select-network-view/index.ts @@ -50,7 +50,6 @@ export class W3mConvertSelectNetworkView extends LitElement { // -- Render -------------------------------------------- // public override render() { - console.log('network', this.network) return html` ${this.templateListNetworks()} ` @@ -70,13 +69,7 @@ export class W3mConvertSelectNetworkView extends LitElement { } private templateNetworkListItem(item: { name: string; symbol: string }, active?: boolean) { - return html` - ${this.detailsOpen - ? html` - - - Network cost - $- - - - - - Service fee - - Free - - - - - - - Fee is paid to Ethereum Network to process your transaction. This must be paid - in ETH. Learn more - - - - ` - : null} - + ` } @@ -324,6 +271,26 @@ export class W3mConvertView extends LitElement { this.onDebouncedGetSwapCalldata() } + private templateActionButton() { + const haveNoTokenSelected = !this.toToken || !this.sourceToken + const loading = this.loadingPrices || this.loading + + return html` + + ${this.actionButtonLabel()} + + ` + } + private onDebouncedGetSwapCalldata = CoreHelperUtil.debounce(async () => { await SwapApiController.getTokenSwapInfo() }, 500) diff --git a/packages/scaffold/src/views/w3m-convert-view/styles.ts b/packages/scaffold/src/views/w3m-convert-view/styles.ts index 7f3edcae2f..75e1a07b9a 100644 --- a/packages/scaffold/src/views/w3m-convert-view/styles.ts +++ b/packages/scaffold/src/views/w3m-convert-view/styles.ts @@ -90,4 +90,10 @@ export default css` border-radius: var(--wui-border-radius-xxs); background: var(--wui-gray-glass-002); } + + .free-badge { + background: rgba(38, 217, 98, 0.15); + border-radius: var(--wui-border-radius-4xs); + padding: 4.5px 6px; + } ` diff --git a/packages/ui/index.ts b/packages/ui/index.ts index 6f77cf9eaa..f86a807653 100644 --- a/packages/ui/index.ts +++ b/packages/ui/index.ts @@ -16,6 +16,7 @@ export * from './src/composites/wui-card-select-loader/index.js' export * from './src/composites/wui-card-select/index.js' export * from './src/composites/wui-chip/index.js' export * from './src/composites/wui-connect-button/index.js' +export * from './src/composites/wui-convert-details/index.js' export * from './src/composites/wui-cta-button/index.js' export * from './src/composites/wui-email-input/index.js' export * from './src/composites/wui-icon-box/index.js' @@ -66,6 +67,7 @@ export * from './src/layout/wui-flex/index.js' export * from './src/layout/wui-grid/index.js' export * from './src/layout/wui-separator/index.js' +export { formatNumberToLocalString } from './src/utils/NumberUtil.js' export { initializeTheming, setColorTheme, setThemeVariables } from './src/utils/ThemeUtil.js' export { UiHelperUtil } from './src/utils/UiHelperUtil.js' export { TransactionUtil } from './src/utils/TransactionUtil.js' diff --git a/packages/ui/src/composites/wui-convert-details/index.ts b/packages/ui/src/composites/wui-convert-details/index.ts new file mode 100644 index 0000000000..be85355885 --- /dev/null +++ b/packages/ui/src/composites/wui-convert-details/index.ts @@ -0,0 +1,113 @@ +import { html, LitElement } from 'lit' +import { property, state } from 'lit/decorators.js' +import { customElement } from '../../utils/WebComponentsUtil.js' +import { resetStyles } from '../../utils/ThemeUtil.js' +import styles from './styles.js' +import { formatNumberToLocalString } from '../../utils/NumberUtil.js' + +@customElement('wui-convert-details') +export class WuiConvertDetails extends LitElement { + public static override styles = [resetStyles, styles] + + // -- State & Properties -------------------------------- // + @property({ type: Boolean }) public defaultOpen = false + + @state() public detailsOpen = this.defaultOpen + + @property() public sourceTokenSymbol?: number + + @property() public sourceTokenPrice?: number + + @property() public toTokenSymbol?: number + + @property() public toTokenConvertedAmount?: number + + @property() public gasPriceInETH?: number + + @property() public gasPriceInUSD?: number + + @property() public valueDifference = { percentage: 0, value: 0 } + + @property() public slippageRate = 0.5 + + @property() public slippageValue?: number + + // -- Render -------------------------------------------- // + public override render() { + return html` + + + + ${this.detailsOpen + ? html` + + + + Network cost + + $${formatNumberToLocalString(this.gasPriceInUSD)} + + + + + + Price impact + + + %${formatNumberToLocalString(this.valueDifference.percentage)} + + + + + + + Max. slippage + + + ${formatNumberToLocalString(this.slippageValue, 6)} ETH + ${this.slippageRate}% + + + + + + + Provider free + + Free + + + + + ` + : null} + + + ` + } + + // -- Private ------------------------------------------- // + private toggleDetails() { + this.detailsOpen = !this.detailsOpen + } +} + +declare global { + interface HTMLElementTagNameMap { + 'wui-convert-details': WuiConvertDetails + } +} diff --git a/packages/ui/src/composites/wui-convert-details/styles.ts b/packages/ui/src/composites/wui-convert-details/styles.ts new file mode 100644 index 0000000000..52ea3aaabd --- /dev/null +++ b/packages/ui/src/composites/wui-convert-details/styles.ts @@ -0,0 +1,53 @@ +import { css } from 'lit' + +export default css` + :host { + width: 100%; + } + + .details-container > wui-flex { + background: var(--wui-gray-glass-002); + border-radius: var(--wui-border-radius-xxs); + width: 100%; + } + + .details-container > wui-flex > button { + border: none; + background: none; + padding: var(--wui-spacing-s); + border-radius: var(--wui-border-radius-xxs); + transition: background 0.2s linear; + } + + .details-container > wui-flex > button:hover { + background: var(--wui-gray-glass-002); + } + + .details-content-container { + padding: var(--wui-spacing-1xs); + display: flex; + align-items: center; + justify-content: center; + } + + .details-content-container > wui-flex { + width: 100%; + } + + .details-row { + width: 100%; + padding: var(--wui-spacing-s) var(--wui-spacing-xl); + border-radius: var(--wui-border-radius-xxs); + background: var(--wui-gray-glass-002); + } + + .details-row.provider-free-row { + padding: var(--wui-spacing-xs); + } + + .free-badge { + background: rgba(38, 217, 98, 0.15); + border-radius: var(--wui-border-radius-4xs); + padding: 4.5px 6px; + } +` diff --git a/packages/ui/src/utils/NumberUtil.ts b/packages/ui/src/utils/NumberUtil.ts new file mode 100644 index 0000000000..db975b0979 --- /dev/null +++ b/packages/ui/src/utils/NumberUtil.ts @@ -0,0 +1,23 @@ +/** + * Format the given number or string to human readable numbers with the given number of decimals + * @param value - The value to format. It could be a number or string. If it's a string, it will be parsed to a float then formatted. + * @param decimals - number of decimals after dot + * @returns + */ +export function formatNumberToLocalString(value: string | number | undefined, decimals = 2) { + if (value === undefined) { + return '0.00' + } + + if (typeof value === 'number') { + return value.toLocaleString('en-US', { + maximumFractionDigits: decimals, + minimumFractionDigits: decimals + }) + } + + return parseFloat(value).toLocaleString('en-US', { + maximumFractionDigits: decimals, + minimumFractionDigits: decimals + }) +} From ae8eed4d08780b67eaa69630d5a72c10581e4655 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Thu, 7 Mar 2024 22:50:46 +0300 Subject: [PATCH 19/96] fix: build issues --- .../scaffold/src/views/w3m-convert-preview-view/index.ts | 9 --------- packages/scaffold/src/views/w3m-convert-view/index.ts | 6 ------ 2 files changed, 15 deletions(-) diff --git a/packages/scaffold/src/views/w3m-convert-preview-view/index.ts b/packages/scaffold/src/views/w3m-convert-preview-view/index.ts index cbc4a41ad6..cc6a761414 100644 --- a/packages/scaffold/src/views/w3m-convert-preview-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-preview-view/index.ts @@ -34,16 +34,12 @@ export class W3mConvertPreviewView extends LitElement { @state() private balanceSymbol = AccountController.state.balanceSymbol - @state() private detailsOpen = true - @state() private gasPriceInUSD = SwapApiController.state.gasPriceInUSD @state() private gasPriceInETH = SwapApiController.state.gasPriceInETH @state() private valueDifference = SwapApiController.state.valueDifference - @state() private swapErrorMessage = SwapApiController.state.swapErrorMessage - // -- Lifecycle ----------------------------------------- // public constructor() { super() @@ -74,7 +70,6 @@ export class W3mConvertPreviewView extends LitElement { this.gasPriceInETH = newState.gasPriceInETH this.sourceTokenAmount = newState.sourceTokenAmount ?? '' this.toTokenAmount = newState.toTokenAmount ?? '' - this.swapErrorMessage = newState.swapErrorMessage }) ] ) @@ -218,10 +213,6 @@ export class W3mConvertPreviewView extends LitElement { private async onSendTransaction() { await SwapApiController.swapTokens() } - - private toggleDetails() { - this.detailsOpen = !this.detailsOpen - } } declare global { diff --git a/packages/scaffold/src/views/w3m-convert-view/index.ts b/packages/scaffold/src/views/w3m-convert-view/index.ts index a390280196..041cb4725d 100644 --- a/packages/scaffold/src/views/w3m-convert-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-view/index.ts @@ -20,8 +20,6 @@ export class W3mConvertView extends LitElement { private unsubscribe: ((() => void) | undefined)[] = [] // -- State & Properties -------------------------------- // - @state() private detailsOpen = false - @state() private caipNetworkId = NetworkController.state.caipNetwork?.id @state() private initialLoading = SwapApiController.state.initialLoading @@ -302,10 +300,6 @@ export class W3mConvertView extends LitElement { private onConvertPreview() { RouterController.push('ConvertPreview') } - - private toggleDetails() { - this.detailsOpen = !this.detailsOpen - } } declare global { From fac33b50545a3c25813234dc402e825a190072c2 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Fri, 8 Mar 2024 16:27:48 +0300 Subject: [PATCH 20/96] refactor: handle TX success --- .../core/src/controllers/SwapApiController.ts | 17 +++++++++++++++++ packages/wagmi/src/client.ts | 4 +++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/packages/core/src/controllers/SwapApiController.ts b/packages/core/src/controllers/SwapApiController.ts index 30bf4da878..da113d1639 100644 --- a/packages/core/src/controllers/SwapApiController.ts +++ b/packages/core/src/controllers/SwapApiController.ts @@ -6,6 +6,7 @@ import { FetchUtil } from '../utils/FetchUtil.js' import { AccountController } from './AccountController.js' import { ConstantsUtil } from '../utils/ConstantsUtil.js' import { ConnectionController } from './ConnectionController.js' +import { RouterController } from './RouterController.js' const ONEINCH_API_BASE_URL = 'https://1inch-swap-proxy.walletconnect-v1-bridge.workers.dev' const CURRENT_CHAIN_ADDRESS = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee' @@ -248,6 +249,19 @@ export const SwapApiController = { state.tokens = undefined }, + resetState() { + state.tokens = undefined + state.myTokensWithBalance = undefined + state.sourceToken = undefined + state.toToken = undefined + state.sourceTokenAmount = '0' + state.toTokenAmount = '0' + state.sourceTokenPriceInUSD = 0 + state.toTokenPriceInUSD = 0 + state.gasPriceInETH = 0 + state.gasPriceInUSD = 0 + }, + clearMyTokens() { state.myTokensWithBalance = undefined }, @@ -604,6 +618,9 @@ export const SwapApiController = { value: BigInt(state.swapTransaction.value) }) + this.resetState() + RouterController.replace('Transactions') + await this.getMyTokensWithBalance({ forceRefetch: true }) } catch (error: unknown) { if (error instanceof Error) { diff --git a/packages/wagmi/src/client.ts b/packages/wagmi/src/client.ts index d4f36023d6..62a234ffb8 100644 --- a/packages/wagmi/src/client.ts +++ b/packages/wagmi/src/client.ts @@ -183,6 +183,7 @@ export class Web3Modal extends Web3ModalScaffold { value: data.value, gas: data.gas, gasPrice: data.gasPrice, + data: data.data, type: 'legacy' }) @@ -192,7 +193,8 @@ export class Web3Modal extends Web3ModalScaffold { value: data.value, gas: data.gas, gasPrice: data.gasPrice, - type: 'legacy' + type: 'legacy', + data: data.data }) await waitForTransactionReceipt(this.wagmiConfig, { hash: tx, timeout: 25000 }) From 452e158f50e43780fa2e3c9d69df822469a9dfe9 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Tue, 12 Mar 2024 01:36:10 +0300 Subject: [PATCH 21/96] feat: calculate price impact and slippage values --- .../core/src/controllers/SwapApiController.ts | 93 +++++++----- .../index.ts | 10 +- .../views/w3m-convert-preview-view/index.ts | 9 +- .../w3m-convert-select-token-view/index.ts | 143 +++++++++++------- .../w3m-convert-select-token-view/styles.ts | 73 +++------ .../src/views/w3m-convert-view/index.ts | 30 +++- .../ui/src/composites/wui-button/index.ts | 2 + .../composites/wui-convert-details/index.ts | 74 +++++---- .../composites/wui-convert-details/styles.ts | 6 +- .../src/composites/wui-convert-input/index.ts | 35 +++-- .../composites/wui-convert-input/styles.ts | 6 +- .../composites/wui-token-list-item/index.ts | 7 +- .../composites/wui-token-list-item/styles.ts | 8 + 13 files changed, 301 insertions(+), 195 deletions(-) diff --git a/packages/core/src/controllers/SwapApiController.ts b/packages/core/src/controllers/SwapApiController.ts index da113d1639..523b3db4fc 100644 --- a/packages/core/src/controllers/SwapApiController.ts +++ b/packages/core/src/controllers/SwapApiController.ts @@ -10,6 +10,7 @@ import { RouterController } from './RouterController.js' const ONEINCH_API_BASE_URL = 'https://1inch-swap-proxy.walletconnect-v1-bridge.workers.dev' const CURRENT_CHAIN_ADDRESS = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee' +export const DEFAULT_SLIPPAGE_TOLERANCE = 0.5 const OneInchAPIEndpoints = { approveTransaction: (chainId: number) => `/swap/v5.2/${chainId}/approve/transaction`, @@ -36,12 +37,13 @@ export interface SwapApiControllerState { toToken?: TokenInfo toTokenAmount: string toTokenPriceInUSD: number - ethPrice: string + networkPrice: string + networkAddress: string | undefined gasPriceInUSD?: number gasPriceInETH?: number swapTransaction?: TransactionData swapApproval?: SwapApprovalData - slippage?: number + slippage: number disableEstimate?: boolean allowPartialFill?: boolean hasAllowance: boolean @@ -53,10 +55,8 @@ export interface SwapApiControllerState { tokensPriceMap: Record swapErrorMessage?: string loadingPrices: boolean - valueDifference: { - percentage: number - direction: PriceDifferenceDirection - } + priceImpact: number | undefined + maxSlippage: number | undefined } export interface TokenInfo { @@ -127,10 +127,11 @@ const state = proxy({ sourceTokenAmount: '', sourceTokenPriceInUSD: 0, toTokenPriceInUSD: 0, - ethPrice: '0', + networkPrice: '0', + networkAddress: CURRENT_CHAIN_ADDRESS, gasPriceInUSD: 0, gasPriceInETH: 0, - slippage: 0.5, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, disableEstimate: false, allowPartialFill: false, initialLoading: false, @@ -144,10 +145,8 @@ const state = proxy({ isTransactionPending: false, loadingPrices: false, swapTransaction: undefined, - valueDifference: { - percentage: 0, - direction: 'none' - } + priceImpact: undefined, + maxSlippage: undefined }) // -- Controller ---------------------------------------- // @@ -200,6 +199,8 @@ export const SwapApiController = { setSourceToken(sourceToken?: TokenInfo) { state.sourceToken = sourceToken + state.sourceTokenAmount = '' + state.toTokenAmount = '' if (sourceToken?.address && !state.tokensPriceMap[sourceToken?.address]) { this.getTokenPriceWithAddresses([sourceToken?.address]) } @@ -335,15 +336,17 @@ export const SwapApiController = { if (state.tokens && !options?.forceRefetch) { return state.tokens } + const networkTokenSymbol = AccountController.state.balanceSymbol const { api, paths } = this._get1inchApi() state.initialLoading = true - - await this.getMyTokensWithBalance({ forceRefetch: true }) - const res = await api.get({ path: paths.tokens }) state.tokens = res.tokens + state.networkAddress = + Object.keys(res.tokens).find(address => res.tokens[address]?.symbol === networkTokenSymbol) || + undefined + state.popularTokens = Object.entries(res.tokens) .sort(([, aTokenInfo], [, bTokenInfo]) => { if (aTokenInfo.symbol < bTokenInfo.symbol) { @@ -363,6 +366,8 @@ export const SwapApiController = { return limitedTokens }, {}) + await this.getMyTokensWithBalance({ forceRefetch: true }) + state.initialLoading = false return state.tokens @@ -394,9 +399,13 @@ export const SwapApiController = { return undefined } + const addresses = state.networkAddress + ? [...tokenAddresses, state.networkAddress] + : tokenAddresses + const [tokens, tokensPrice] = await Promise.all([ - this.getTokenInfoWithAddresses(tokenAddresses), - this.getTokenPriceWithAddresses(tokenAddresses) + this.getTokenInfoWithAddresses(addresses), + this.getTokenPriceWithAddresses(addresses) ]) const mergedTokensWithBalances = this.mergeTokenWithBalances(tokens, balances, tokensPrice) @@ -450,14 +459,7 @@ export const SwapApiController = { } }) - const chainId = CoreHelperUtil.getEvmChainId(NetworkController.state.caipNetwork?.id) - const isMainnet = chainId === 1 - - if (isMainnet) { - state.ethPrice = prices[CURRENT_CHAIN_ADDRESS] || '0' - } else { - state.ethPrice = '0' - } + state.networkPrice = state.networkAddress ? prices[state.networkAddress] || '0' : '0' Object.entries(prices).forEach(([tokenAddress, price]) => { state.tokensPriceMap[tokenAddress] = price @@ -500,12 +502,33 @@ export const SwapApiController = { calculateGasPriceInUSD(gas: number, gasPrice: string) { const totalGasCostInEther = this.calculateGasPriceInETH(gas, gasPrice) - const ethPriceNumber = Number(state.ethPrice) - const totalCostInUSD = totalGasCostInEther * ethPriceNumber + const networkPriceNumber = parseFloat(state.networkPrice) + const totalCostInUSD = totalGasCostInEther * networkPriceNumber + console.log('>>> networkPriceNumber', networkPriceNumber, totalGasCostInEther, totalCostInUSD) return totalCostInUSD }, + calculatePriceImpact(_toTokenAmount: string) { + const sourceTokenAmount = parseFloat(state.sourceTokenAmount) + const toTokenAmount = parseFloat(_toTokenAmount) + const sourceTokenPrice = state.sourceTokenPriceInUSD + const toTokenPrice = state.toTokenPriceInUSD + + const effectivePrice = (sourceTokenAmount * sourceTokenPrice) / toTokenAmount + const priceImpact = ((effectivePrice - toTokenPrice) / toTokenPrice) * 100 + + return priceImpact + }, + + calculateMaxSlippage() { + const fromTokenAmount = parseFloat(state.sourceTokenAmount) + const slippageToleranceDecimal = state.slippage / 100 + const maxSlippageAmount = fromTokenAmount * slippageToleranceDecimal + + return maxSlippageAmount + }, + async getTokenSwapInfo() { if (state.sourceToken?.address && state.toToken?.address) { state.loading = true @@ -560,21 +583,19 @@ export const SwapApiController = { const swapTransaction: SwapResponse = swapTransactionRes state.swapTransaction = swapTransaction.tx - state.gasPriceInUSD = this.calculateGasPriceInUSD( - swapTransaction.tx.gas, - swapTransaction.tx.gasPrice - ) - state.gasPriceInETH = this.calculateGasPriceInETH( - swapTransaction.tx.gas, - swapTransaction.tx.gasPrice - ) + const transaction = swapTransaction.tx + state.toTokenAmount = ConnectionController.formatUnits( BigInt(swapTransaction.toAmount), state.toToken.decimals ) const toTokenPrice = state.tokensPriceMap[state.toToken.address] || '0' state.toTokenPriceInUSD = parseFloat(toTokenPrice) - state.valueDifference = this.calculatePriceDifference() + + state.priceImpact = this.calculatePriceImpact(state.toTokenAmount) + state.maxSlippage = this.calculateMaxSlippage() + state.gasPriceInUSD = this.calculateGasPriceInUSD(transaction.gas, transaction.gasPrice) + state.gasPriceInETH = this.calculateGasPriceInETH(transaction.gas, transaction.gasPrice) } }, diff --git a/packages/scaffold/src/partials/w3m-account-wallet-features-widget/index.ts b/packages/scaffold/src/partials/w3m-account-wallet-features-widget/index.ts index 7ce22d743f..569b984927 100644 --- a/packages/scaffold/src/partials/w3m-account-wallet-features-widget/index.ts +++ b/packages/scaffold/src/partials/w3m-account-wallet-features-widget/index.ts @@ -91,7 +91,11 @@ export class W3mAccountWalletFeaturesWidget extends LitElement { text="Buy" icon="card" > - + diff --git a/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts b/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts index 84d90ced39..765770b5ec 100644 --- a/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts @@ -9,11 +9,27 @@ import { state } from 'lit/decorators.js' export class W3mConvertSelectTokenView extends LitElement { public static override styles = styles + private unsubscribe: ((() => void) | undefined)[] = [] + + // -- State & Properties -------------------------------- // @state() private targetToken = RouterController.state.data?.target + @state() private sourceToken = SwapApiController.state.sourceToken + + @state() private toToken = SwapApiController.state.toToken + // -- Lifecycle ----------------------------------------- // public constructor() { super() + + this.unsubscribe.push( + ...[ + SwapApiController.subscribe(newState => { + this.sourceToken = newState.sourceToken + this.toToken = newState.toToken + }) + ] + ) } private onSelectToken(token: TokenInfo) { @@ -63,34 +79,42 @@ export class W3mConvertSelectTokenView extends LitElement { private templateTokens() { return html` - - ${SwapApiController.state.myTokensWithBalance && - html` - Your tokens - `} - - - ${SwapApiController.state.myTokensWithBalance && - Object.values(SwapApiController.state.myTokensWithBalance).map( - tokenInfo => html` - this.onSelectToken(tokenInfo)} - > - + + ${SwapApiController.state.myTokensWithBalance + ? html` + + Your tokens + + + ${Object.values(SwapApiController.state.myTokensWithBalance).map(tokenInfo => { + const selected = + tokenInfo.symbol === this.sourceToken?.symbol || + tokenInfo.symbol === this.toToken?.symbol + return html` + { + if (!selected) this.onSelectToken(tokenInfo) + }} + > + + ` + })} ` - )} - + : null} + Popular tokens + ${SwapApiController.state.popularTokens && Object.values(SwapApiController.state.popularTokens).map( @@ -120,7 +144,7 @@ export class W3mConvertSelectTokenView extends LitElement { } return html` - + ${tokens.map( tokenInfo => html` originalRangeMax) return newRangeMax + + return ( + ((newRangeMax - newRangeMin) / (originalRangeMax - originalRangeMin)) * + (value - originalRangeMin) + + newRangeMin + ) + } + private handleSuggestedTokensScroll() { const container = this.renderRoot?.querySelector('.suggested-tokens-container') as | HTMLElement @@ -144,20 +184,18 @@ export class W3mConvertSelectTokenView extends LitElement { return } - const scrollStart = container.scrollLeft === 0 - const scrollEnd = - Math.abs(Math.round(container.scrollLeft + container.offsetWidth) - container.scrollWidth) < 2 - - if (scrollStart) { - container.classList.add('scroll-start') - container.classList.remove('scroll-end') - } else if (scrollEnd) { - container.classList.add('scroll-end') - container.classList.remove('scroll-start') - } else { - container.classList.remove('scroll-start') - container.classList.remove('scroll-end') - } + container.style.setProperty( + '--suggested-tokens-scroll--left-opacity', + this.interpolate([0, 100], [0, 1], container.scrollLeft).toString() + ) + container.style.setProperty( + '--suggested-tokens-scroll--right-opacity', + this.interpolate( + [0, 100], + [0, 1], + container.scrollWidth - container.scrollLeft - container.offsetWidth + ).toString() + ) } private handleTokenListScroll() { @@ -167,21 +205,18 @@ export class W3mConvertSelectTokenView extends LitElement { return } - const scrollStart = container.scrollTop === 0 - const scrollEnd = - Math.abs(Math.round(container.scrollTop + container.offsetHeight) - container.scrollHeight) < - 2 - - if (scrollStart) { - container.classList.add('scroll-start') - container.classList.remove('scroll-end') - } else if (scrollEnd) { - container.classList.add('scroll-end') - container.classList.remove('scroll-start') - } else { - container.classList.remove('scroll-start') - container.classList.remove('scroll-end') - } + container.style.setProperty( + '--tokens-scroll--top-opacity', + this.interpolate([0, 100], [0, 1], container.scrollTop).toString() + ) + container.style.setProperty( + '--tokens-scroll--bottom-opacity', + this.interpolate( + [0, 100], + [0, 1], + container.scrollHeight - container.scrollTop - container.offsetHeight + ).toString() + ) } public override disconnectedCallback() { diff --git a/packages/scaffold/src/views/w3m-convert-select-token-view/styles.ts b/packages/scaffold/src/views/w3m-convert-select-token-view/styles.ts index 370b9a7717..b5aac803f8 100644 --- a/packages/scaffold/src/views/w3m-convert-select-token-view/styles.ts +++ b/packages/scaffold/src/views/w3m-convert-select-token-view/styles.ts @@ -13,31 +13,32 @@ export default css` } .search-input-container, - .suggested-tokens-container, - .tokens-container { + .suggested-tokens-container { padding-left: var(--wui-spacing-s); padding-right: var(--wui-spacing-s); } + .tokens-container .tokens { + padding: 0px var(--wui-spacing-s); + padding-bottom: var(--wui-spacing-s); + } + .search-input-container { padding-top: var(--wui-spacing-s); } .suggested-tokens-container { overflow-x: auto; - -webkit-mask-image: linear-gradient( - to right, - transparent 0px, - black 44px, - black calc(100% - 44px), - transparent 100% - ); mask-image: linear-gradient( to right, - transparent 0px, - black 44px, - black calc(100% - 44px), - transparent 100% + rgba(0, 0, 0, calc(1 - var(--suggested-tokens-scroll--left-opacity))) 0px, + rgba(200, 200, 200, calc(1 - var(--suggested-tokens-scroll--left-opacity))) 1px, + black 50px, + black 90px, + black calc(100% - 90px), + black calc(100% - 50px), + rgba(155, 155, 155, calc(1 - var(--suggested-tokens-scroll--right-opacity))) calc(100% - 1px), + rgba(0, 0, 0, calc(1 - var(--suggested-tokens-scroll--right-opacity))) 100% ); } @@ -45,54 +46,28 @@ export default css` display: none; } - .suggested-tokens-container.scroll-start { - -webkit-mask-image: linear-gradient( - to right, - black 0px, - black calc(100% - 44px), - transparent 100% - ); - mask-image: linear-gradient(to right, black 0px, black calc(100% - 44px), transparent 100%); - } - - .suggested-tokens-container.scroll-end { - -webkit-mask-image: linear-gradient(to right, transparent 0px, black 44px, black 100%); - mask-image: linear-gradient(to right, transparent 0px, black 44px, black 100%); - } - .tokens-container { border-top: 1px solid var(--wui-gray-glass-005); height: 100%; - max-height: calc(512px); + max-height: 390px; } .tokens { width: 100%; overflow-y: auto; - -webkit-mask-image: linear-gradient( - transparent 0px, - black 44px, - black calc(100% - 44px), - transparent 100% - ); mask-image: linear-gradient( - transparent 0px, - black 44px, - black calc(100% - 44px), - transparent 100% + to bottom, + rgba(0, 0, 0, calc(1 - var(--tokens-scroll--top-opacity))) 0px, + rgba(200, 200, 200, calc(1 - var(--tokens-scroll--top-opacity))) 1px, + black 50px, + black 90px, + black calc(100% - 90px), + black calc(100% - 50px), + rgba(155, 155, 155, calc(1 - var(--tokens-scroll--bottom-opacity))) calc(100% - 1px), + rgba(0, 0, 0, calc(1 - var(--tokens-scroll--bottom-opacity))) 100% ); } - .tokens.scroll-start { - -webkit-mask-image: linear-gradient(black 0px, black calc(100% - 44px), transparent 100%); - mask-image: linear-gradient(black 0px, black calc(100% - 44px), transparent 100%); - } - - .tokens.scroll-end { - -webkit-mask-image: linear-gradient(transparent 0px, black 44px, black 100%); - mask-image: linear-gradient(transparent 0px, black 44px, black 100%); - } - .network-search-input, .select-network-button { height: 40px; diff --git a/packages/scaffold/src/views/w3m-convert-view/index.ts b/packages/scaffold/src/views/w3m-convert-view/index.ts index 041cb4725d..bc0d410d31 100644 --- a/packages/scaffold/src/views/w3m-convert-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-view/index.ts @@ -7,7 +7,8 @@ import { RouterController, CoreHelperUtil, NetworkController, - ConnectionController + ConnectionController, + ModalController } from '@web3modal/core' import type { TokenInfo } from '@web3modal/core/src/controllers/SwapApiController.js' @@ -48,7 +49,9 @@ export class W3mConvertView extends LitElement { @state() private gasPriceInETH = SwapApiController.state.gasPriceInETH - @state() private valueDifference = SwapApiController.state.valueDifference + @state() private priceImpact = SwapApiController.state.priceImpact + + @state() private maxSlippage = SwapApiController.state.maxSlippage @state() private swapErrorMessage = SwapApiController.state.swapErrorMessage @@ -69,6 +72,16 @@ export class W3mConvertView extends LitElement { this.unsubscribe.push( ...[ + ModalController.subscribeKey('open', isOpen => { + if (!isOpen) { + SwapApiController.resetState() + } + }), + RouterController.subscribeKey('view', newRoute => { + if (!newRoute.includes('Convert')) { + SwapApiController.resetState() + } + }), SwapApiController.subscribeKey('sourceToken', newSourceToken => { this.sourceToken = newSourceToken }), @@ -89,8 +102,9 @@ export class W3mConvertView extends LitElement { this.hasAllowance = newState.hasAllowance this.gasPriceInUSD = newState.gasPriceInUSD this.gasPriceInETH = newState.gasPriceInETH - this.valueDifference = newState.valueDifference this.swapErrorMessage = newState.swapErrorMessage + this.priceImpact = newState.priceImpact + this.maxSlippage = newState.maxSlippage }) ] ) @@ -227,7 +241,7 @@ export class W3mConvertView extends LitElement { target=${target} .token=${token} .balance=${myToken?.balance} - .marketValue=${formatNumberToLocalString(value)} + .marketValue=${isNaN(value) ? '' : formatNumberToLocalString(value)} amount=${myToken ? formatNumberToLocalString( ConnectionController.formatUnits(BigInt(myToken.balance), myToken.decimals), @@ -238,6 +252,10 @@ export class W3mConvertView extends LitElement { } private templateDetails() { + if (!this.sourceToken || !this.toToken || !this.sourceTokenAmount || !this.toTokenAmount) { + return null + } + const toTokenConvertedAmount = this.sourceTokenPriceInUSD && this.toTokenPriceInUSD ? (1 / this.toTokenPriceInUSD) * this.sourceTokenPriceInUSD @@ -252,9 +270,9 @@ export class W3mConvertView extends LitElement { toTokenConvertedAmount=${toTokenConvertedAmount} gasPriceInETH=${this.gasPriceInETH} gasPriceInUSD=${this.gasPriceInUSD} - .valueDifference=${this.valueDifference} + .priceImpact=${this.priceImpact} slippageRate=${0.5} - slippageValue=${this.gasPriceInETH} + .maxSlippage=${this.maxSlippage} > ` } diff --git a/packages/ui/src/composites/wui-button/index.ts b/packages/ui/src/composites/wui-button/index.ts index 1365b95d9f..624730114b 100644 --- a/packages/ui/src/composites/wui-button/index.ts +++ b/packages/ui/src/composites/wui-button/index.ts @@ -42,6 +42,8 @@ export class WuiButton extends LitElement { public override render() { this.style.cssText = ` --local-width: ${this.fullWidth ? '100%' : 'auto'}; + --local-border-color: ${this.fullWidth ? '100%' : 'auto'}; + --local-background-color: ${this.fullWidth ? '100%' : 'auto'}; --local-opacity-100: ${this.loading ? 0 : 1}; --local-opacity-000: ${this.loading ? 1 : 0}; --local-border-radius: var(--wui-border-radius-${this.borderRadius}); diff --git a/packages/ui/src/composites/wui-convert-details/index.ts b/packages/ui/src/composites/wui-convert-details/index.ts index be85355885..2a686be61e 100644 --- a/packages/ui/src/composites/wui-convert-details/index.ts +++ b/packages/ui/src/composites/wui-convert-details/index.ts @@ -26,11 +26,11 @@ export class WuiConvertDetails extends LitElement { @property() public gasPriceInUSD?: number - @property() public valueDifference = { percentage: 0, value: 0 } + @property() public priceImpact?: number @property() public slippageRate = 0.5 - @property() public slippageValue?: number + @property() public maxSlippage?: number // -- Render -------------------------------------------- // public override render() { @@ -56,37 +56,57 @@ export class WuiConvertDetails extends LitElement { ? html` - + Network cost - $${formatNumberToLocalString(this.gasPriceInUSD)} + $${formatNumberToLocalString(this.gasPriceInUSD, 3)} + ${this.priceImpact + ? html` + + Price impact + + + ${formatNumberToLocalString(this.priceImpact, 3)}% + + + + ` + : null} + ${this.maxSlippage && this.sourceTokenSymbol + ? html` + + Max. slippage + + + ${formatNumberToLocalString(this.maxSlippage, 6)} + ${this.sourceTokenSymbol} ${this.slippageRate}% + + + + ` + : null} - - Price impact - - - %${formatNumberToLocalString(this.valueDifference.percentage)} - - - - - - - Max. slippage - - - ${formatNumberToLocalString(this.slippageValue, 6)} ETH - ${this.slippageRate}% - - - - - - - Provider free + + Provider fee Free diff --git a/packages/ui/src/composites/wui-convert-details/styles.ts b/packages/ui/src/composites/wui-convert-details/styles.ts index 52ea3aaabd..3436243dec 100644 --- a/packages/ui/src/composites/wui-convert-details/styles.ts +++ b/packages/ui/src/composites/wui-convert-details/styles.ts @@ -36,13 +36,15 @@ export default css` .details-row { width: 100%; - padding: var(--wui-spacing-s) var(--wui-spacing-xl); + padding: var(--wui-spacing-s); + padding-left: var(--wui-spacing-s); + padding-right: calc(2px + var(--wui-spacing-xs)); border-radius: var(--wui-border-radius-xxs); background: var(--wui-gray-glass-002); } .details-row.provider-free-row { - padding: var(--wui-spacing-xs); + padding-right: var(--wui-spacing-xs); } .free-badge { diff --git a/packages/ui/src/composites/wui-convert-input/index.ts b/packages/ui/src/composites/wui-convert-input/index.ts index 24325c21b5..c8de0d60f7 100644 --- a/packages/ui/src/composites/wui-convert-input/index.ts +++ b/packages/ui/src/composites/wui-convert-input/index.ts @@ -62,9 +62,9 @@ export class WuiConvertInput extends LitElement { @keydown=${this.handleKeydown} placeholder="0" /> - ${this.value - ? html`$${this.marketValue}` - : null} + + ${this.marketValue ? `$${this.marketValue}` : ''} + ${this.templateTokenSelectButton()} @@ -73,7 +73,18 @@ export class WuiConvertInput extends LitElement { // -- Private ------------------------------------------- // private handleKeydown(event: KeyboardEvent) { - const allowedKeys = ['Backspace', 'ArrowLeft', 'ArrowRight', 'Tab'] + console.log('>>> keypress', event) + const allowedKeys = [ + 'Backspace', + 'Meta', + 'Ctrl', + 'a', + 'c', + 'v', + 'ArrowLeft', + 'ArrowRight', + 'Tab' + ] const isComma = event.key === ',' const isDot = event.key === '.' const isNumericKey = event.key >= '0' && event.key <= '9' @@ -140,14 +151,14 @@ export class WuiConvertInput extends LitElement { ${tokenElement} ${this.token.symbol} - ${this.target === 'sourceToken' && this.amount && parseFloat(this.amount) - ? html` - ${this.amount} - - ` - : null} + + ${this.amount} + ${this.target === 'sourceToken' + ? html` ` + : null} + ` } diff --git a/packages/ui/src/composites/wui-convert-input/styles.ts b/packages/ui/src/composites/wui-convert-input/styles.ts index 08a8cd2318..812eed5d17 100644 --- a/packages/ui/src/composites/wui-convert-input/styles.ts +++ b/packages/ui/src/composites/wui-convert-input/styles.ts @@ -46,7 +46,7 @@ export default css` :host > wui-flex .swap-input input { background: none; border: none; - height: 48px; + height: 42px; width: 100%; font-size: 32px; font-style: normal; @@ -99,4 +99,8 @@ export default css` cursor: pointer; color: var(--wui-gray-glass-020); } + + .market-value { + min-height: 18px; + } ` diff --git a/packages/ui/src/composites/wui-token-list-item/index.ts b/packages/ui/src/composites/wui-token-list-item/index.ts index 705c9e8b83..67ec9c7fc6 100644 --- a/packages/ui/src/composites/wui-token-list-item/index.ts +++ b/packages/ui/src/composites/wui-token-list-item/index.ts @@ -9,6 +9,7 @@ import { elementStyles, resetStyles } from '../../utils/ThemeUtil.js' import { customElement } from '../../utils/WebComponentsUtil.js' import '../wui-icon-box/index.js' import styles from './styles.js' +import { formatNumberToLocalString } from '../../utils/NumberUtil.js' @customElement('wui-token-list-item') export class WuiTokenListItem extends LitElement { @@ -42,7 +43,9 @@ export class WuiTokenListItem extends LitElement { ${this.symbol} ${this.amount && - html`${this.amount}`} + html`${formatNumberToLocalString(this.amount, 4)}`} @@ -50,7 +53,7 @@ export class WuiTokenListItem extends LitElement { } // -- Private ------------------------------------------- // - public visualTemplate() { + private visualTemplate() { if (this.imageSrc) { return html`` } diff --git a/packages/ui/src/composites/wui-token-list-item/styles.ts b/packages/ui/src/composites/wui-token-list-item/styles.ts index bb4732624e..488ecfd1c0 100644 --- a/packages/ui/src/composites/wui-token-list-item/styles.ts +++ b/packages/ui/src/composites/wui-token-list-item/styles.ts @@ -17,6 +17,14 @@ export default css` background-color: var(--wui-gray-glass-002); } + :host([disabled]) > wui-flex { + opacity: 0.6; + } + + :host([disabled]) > wui-flex:hover { + background-color: transparent; + } + :host > wui-flex > wui-flex { flex: 1; } From 25bca0ba9b57bec23b17865ec9743539c6df751d Mon Sep 17 00:00:00 2001 From: enesozturk Date: Tue, 12 Mar 2024 01:39:30 +0300 Subject: [PATCH 22/96] chore: remove unused variables --- packages/core/src/controllers/SwapApiController.ts | 8 ++------ .../scaffold/src/views/w3m-convert-preview-view/index.ts | 9 ++++----- packages/scaffold/src/views/w3m-convert-view/index.ts | 4 ---- packages/ui/src/composites/wui-convert-details/index.ts | 2 -- 4 files changed, 6 insertions(+), 17 deletions(-) diff --git a/packages/core/src/controllers/SwapApiController.ts b/packages/core/src/controllers/SwapApiController.ts index 523b3db4fc..cb79a57269 100644 --- a/packages/core/src/controllers/SwapApiController.ts +++ b/packages/core/src/controllers/SwapApiController.ts @@ -40,7 +40,6 @@ export interface SwapApiControllerState { networkPrice: string networkAddress: string | undefined gasPriceInUSD?: number - gasPriceInETH?: number swapTransaction?: TransactionData swapApproval?: SwapApprovalData slippage: number @@ -130,7 +129,6 @@ const state = proxy({ networkPrice: '0', networkAddress: CURRENT_CHAIN_ADDRESS, gasPriceInUSD: 0, - gasPriceInETH: 0, slippage: DEFAULT_SLIPPAGE_TOLERANCE, disableEstimate: false, allowPartialFill: false, @@ -259,7 +257,6 @@ export const SwapApiController = { state.toTokenAmount = '0' state.sourceTokenPriceInUSD = 0 state.toTokenPriceInUSD = 0 - state.gasPriceInETH = 0 state.gasPriceInUSD = 0 }, @@ -492,7 +489,7 @@ export const SwapApiController = { ) }, - calculateGasPriceInETH(gas: number, gasPrice: string) { + calculateGasPriceInEther(gas: number, gasPrice: string) { const gasPriceNumber = BigInt(gasPrice) const totalGasCostInWei = gasPriceNumber * BigInt(gas) const totalGasCostInEther = Number(totalGasCostInWei) / 1e18 @@ -501,7 +498,7 @@ export const SwapApiController = { }, calculateGasPriceInUSD(gas: number, gasPrice: string) { - const totalGasCostInEther = this.calculateGasPriceInETH(gas, gasPrice) + const totalGasCostInEther = this.calculateGasPriceInEther(gas, gasPrice) const networkPriceNumber = parseFloat(state.networkPrice) const totalCostInUSD = totalGasCostInEther * networkPriceNumber console.log('>>> networkPriceNumber', networkPriceNumber, totalGasCostInEther, totalCostInUSD) @@ -595,7 +592,6 @@ export const SwapApiController = { state.priceImpact = this.calculatePriceImpact(state.toTokenAmount) state.maxSlippage = this.calculateMaxSlippage() state.gasPriceInUSD = this.calculateGasPriceInUSD(transaction.gas, transaction.gasPrice) - state.gasPriceInETH = this.calculateGasPriceInETH(transaction.gas, transaction.gasPrice) } }, diff --git a/packages/scaffold/src/views/w3m-convert-preview-view/index.ts b/packages/scaffold/src/views/w3m-convert-preview-view/index.ts index c2b6969363..e250c0fd8c 100644 --- a/packages/scaffold/src/views/w3m-convert-preview-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-preview-view/index.ts @@ -36,10 +36,10 @@ export class W3mConvertPreviewView extends LitElement { @state() private gasPriceInUSD = SwapApiController.state.gasPriceInUSD - @state() private gasPriceInETH = SwapApiController.state.gasPriceInETH - @state() private priceImpact = SwapApiController.state.priceImpact + @state() private maxSlippage = SwapApiController.state.maxSlippage + // -- Lifecycle ----------------------------------------- // public constructor() { super() @@ -65,10 +65,10 @@ export class W3mConvertPreviewView extends LitElement { this.gasPriceInUSD = newState.gasPriceInUSD this.isTransactionPending = newState.isTransactionPending this.toTokenPriceInUSD = newState.toTokenPriceInUSD - this.gasPriceInETH = newState.gasPriceInETH this.sourceTokenAmount = newState.sourceTokenAmount ?? '' this.toTokenAmount = newState.toTokenAmount ?? '' this.priceImpact = newState.priceImpact + this.maxSlippage = newState.maxSlippage }) ] ) @@ -196,11 +196,10 @@ export class W3mConvertPreviewView extends LitElement { sourceTokenPrice=${this.sourceTokenPriceInUSD} toTokenSymbol=${this.toToken?.symbol} toTokenConvertedAmount=${toTokenConvertedAmount} - gasPriceInETH=${this.gasPriceInETH} gasPriceInUSD=${formatNumberToLocalString(this.gasPriceInUSD, 3)} .priceImpact=${this.priceImpact} slippageRate=${0.5} - slippageValue=${this.gasPriceInETH} + .maxSlippage=${this.maxSlippage} > ` } diff --git a/packages/scaffold/src/views/w3m-convert-view/index.ts b/packages/scaffold/src/views/w3m-convert-view/index.ts index bc0d410d31..504707a99f 100644 --- a/packages/scaffold/src/views/w3m-convert-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-view/index.ts @@ -47,8 +47,6 @@ export class W3mConvertView extends LitElement { @state() private gasPriceInUSD = SwapApiController.state.gasPriceInUSD - @state() private gasPriceInETH = SwapApiController.state.gasPriceInETH - @state() private priceImpact = SwapApiController.state.priceImpact @state() private maxSlippage = SwapApiController.state.maxSlippage @@ -101,7 +99,6 @@ export class W3mConvertView extends LitElement { this.toTokenPriceInUSD = newState.toTokenPriceInUSD this.hasAllowance = newState.hasAllowance this.gasPriceInUSD = newState.gasPriceInUSD - this.gasPriceInETH = newState.gasPriceInETH this.swapErrorMessage = newState.swapErrorMessage this.priceImpact = newState.priceImpact this.maxSlippage = newState.maxSlippage @@ -268,7 +265,6 @@ export class W3mConvertView extends LitElement { sourceTokenPrice=${this.sourceTokenPriceInUSD} toTokenSymbol=${this.toToken?.symbol} toTokenConvertedAmount=${toTokenConvertedAmount} - gasPriceInETH=${this.gasPriceInETH} gasPriceInUSD=${this.gasPriceInUSD} .priceImpact=${this.priceImpact} slippageRate=${0.5} diff --git a/packages/ui/src/composites/wui-convert-details/index.ts b/packages/ui/src/composites/wui-convert-details/index.ts index 2a686be61e..3adbc69fed 100644 --- a/packages/ui/src/composites/wui-convert-details/index.ts +++ b/packages/ui/src/composites/wui-convert-details/index.ts @@ -22,8 +22,6 @@ export class WuiConvertDetails extends LitElement { @property() public toTokenConvertedAmount?: number - @property() public gasPriceInETH?: number - @property() public gasPriceInUSD?: number @property() public priceImpact?: number From c1d511da4c0f66d5a8b979afbed7009e60d48c97 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Tue, 12 Mar 2024 01:53:42 +0300 Subject: [PATCH 23/96] feat: add search, separate utils --- .../w3m-convert-select-token-view/index.ts | 93 ++++++++++--------- packages/ui/index.ts | 3 +- packages/ui/src/utils/Math.ts | 22 +++++ 3 files changed, 75 insertions(+), 43 deletions(-) create mode 100644 packages/ui/src/utils/Math.ts diff --git a/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts b/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts index 765770b5ec..633332238b 100644 --- a/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts @@ -1,4 +1,4 @@ -import { customElement } from '@web3modal/ui' +import { customElement, interpolate } from '@web3modal/ui' import { LitElement, html } from 'lit' import styles from './styles.js' import { ConnectionController, RouterController, SwapApiController } from '@web3modal/core' @@ -18,6 +18,8 @@ export class W3mConvertSelectTokenView extends LitElement { @state() private toToken = SwapApiController.state.toToken + @state() private searchValue = '' + // -- Lifecycle ----------------------------------------- // public constructor() { super() @@ -53,6 +55,18 @@ export class W3mConvertSelectTokenView extends LitElement { tokensList?.addEventListener('scroll', this.handleTokenListScroll.bind(this)) } + public override disconnectedCallback() { + super.disconnectedCallback() + const suggestedTokensContainer = this.renderRoot?.querySelector('.suggested-tokens-container') + const tokensList = this.renderRoot?.querySelector('.tokens') + + suggestedTokensContainer?.removeEventListener( + 'scroll', + this.handleSuggestedTokensScroll.bind(this) + ) + tokensList?.removeEventListener('scroll', this.handleTokenListScroll.bind(this)) + } + // -- Render -------------------------------------------- // public override render() { return html` @@ -71,22 +85,34 @@ export class W3mConvertSelectTokenView extends LitElement { size="sm" placeholder="Search token" icon="search" + .value=${this.searchValue} + @inputChange=${this.onSearchInputChange.bind(this)} > ` } private templateTokens() { + const yourTokens = SwapApiController.state.myTokensWithBalance + ? Object.values(SwapApiController.state.myTokensWithBalance) + : [] + const tokens = SwapApiController.state.popularTokens + ? Object.values(SwapApiController.state.popularTokens) + : [] + + const filteredYourTokens = this.filterTokensWithText(yourTokens, this.searchValue) + const filteredTokens = this.filterTokensWithText(tokens, this.searchValue) + return html` - ${SwapApiController.state.myTokensWithBalance + ${filteredYourTokens?.length > 0 ? html` Your tokens - ${Object.values(SwapApiController.state.myTokensWithBalance).map(tokenInfo => { + ${filteredYourTokens.map(tokenInfo => { const selected = tokenInfo.symbol === this.sourceToken?.symbol || tokenInfo.symbol === this.toToken?.symbol @@ -116,18 +142,19 @@ export class W3mConvertSelectTokenView extends LitElement { - ${SwapApiController.state.popularTokens && - Object.values(SwapApiController.state.popularTokens).map( - tokenInfo => html` - this.onSelectToken(tokenInfo)} - > - - ` - )} + ${filteredTokens?.length > 0 + ? filteredTokens.map( + tokenInfo => html` + this.onSelectToken(tokenInfo)} + > + + ` + ) + : null} @@ -159,20 +186,8 @@ export class W3mConvertSelectTokenView extends LitElement { ` } - private interpolate(inputRange: number[], outputRange: number[], value: number) { - const originalRangeMin = inputRange[0] as number - const originalRangeMax = inputRange[1] as number - const newRangeMin = outputRange[0] as number - const newRangeMax = outputRange[1] as number - - if (value < originalRangeMin) return newRangeMin - if (value > originalRangeMax) return newRangeMax - - return ( - ((newRangeMax - newRangeMin) / (originalRangeMax - originalRangeMin)) * - (value - originalRangeMin) + - newRangeMin - ) + private onSearchInputChange(event: CustomEvent) { + this.searchValue = event.detail } private handleSuggestedTokensScroll() { @@ -186,11 +201,11 @@ export class W3mConvertSelectTokenView extends LitElement { container.style.setProperty( '--suggested-tokens-scroll--left-opacity', - this.interpolate([0, 100], [0, 1], container.scrollLeft).toString() + interpolate([0, 100], [0, 1], container.scrollLeft).toString() ) container.style.setProperty( '--suggested-tokens-scroll--right-opacity', - this.interpolate( + interpolate( [0, 100], [0, 1], container.scrollWidth - container.scrollLeft - container.offsetWidth @@ -207,11 +222,11 @@ export class W3mConvertSelectTokenView extends LitElement { container.style.setProperty( '--tokens-scroll--top-opacity', - this.interpolate([0, 100], [0, 1], container.scrollTop).toString() + interpolate([0, 100], [0, 1], container.scrollTop).toString() ) container.style.setProperty( '--tokens-scroll--bottom-opacity', - this.interpolate( + interpolate( [0, 100], [0, 1], container.scrollHeight - container.scrollTop - container.offsetHeight @@ -219,16 +234,10 @@ export class W3mConvertSelectTokenView extends LitElement { ) } - public override disconnectedCallback() { - super.disconnectedCallback() - const suggestedTokensContainer = this.renderRoot?.querySelector('.suggested-tokens-container') - const tokensList = this.renderRoot?.querySelector('.tokens') - - suggestedTokensContainer?.removeEventListener( - 'scroll', - this.handleSuggestedTokensScroll.bind(this) + private filterTokensWithText(tokens: TokenInfo[], text: string) { + return tokens.filter(token => + `${token.symbol} ${token.name} ${token.address}`.toLowerCase().includes(text.toLowerCase()) ) - tokensList?.removeEventListener('scroll', this.handleTokenListScroll.bind(this)) } } diff --git a/packages/ui/index.ts b/packages/ui/index.ts index f86a807653..ccc26a8ae6 100644 --- a/packages/ui/index.ts +++ b/packages/ui/index.ts @@ -67,7 +67,8 @@ export * from './src/layout/wui-flex/index.js' export * from './src/layout/wui-grid/index.js' export * from './src/layout/wui-separator/index.js' -export { formatNumberToLocalString } from './src/utils/NumberUtil.js' +export * from './src/utils/Math.js' +export * from './src/utils/NumberUtil.js' export { initializeTheming, setColorTheme, setThemeVariables } from './src/utils/ThemeUtil.js' export { UiHelperUtil } from './src/utils/UiHelperUtil.js' export { TransactionUtil } from './src/utils/TransactionUtil.js' diff --git a/packages/ui/src/utils/Math.ts b/packages/ui/src/utils/Math.ts new file mode 100644 index 0000000000..078401deb7 --- /dev/null +++ b/packages/ui/src/utils/Math.ts @@ -0,0 +1,22 @@ +/** + * Interpolates a value from one range to another + * @param inputRange - number array of length 2 that represents the original range + * @param outputRange - number array of length 2 that represents the new range + * @param value - the value to interpolation + * @returns + */ +export function interpolate(inputRange: number[], outputRange: number[], value: number) { + const originalRangeMin = inputRange[0] as number + const originalRangeMax = inputRange[1] as number + const newRangeMin = outputRange[0] as number + const newRangeMax = outputRange[1] as number + + if (value < originalRangeMin) return newRangeMin + if (value > originalRangeMax) return newRangeMax + + return ( + ((newRangeMax - newRangeMin) / (originalRangeMax - originalRangeMin)) * + (value - originalRangeMin) + + newRangeMin + ) +} From a7ec5b9f78e68e3b9c0ca3592e97cb44aad8b72c Mon Sep 17 00:00:00 2001 From: enesozturk Date: Tue, 12 Mar 2024 01:58:16 +0300 Subject: [PATCH 24/96] fix: linter issues --- .../core/src/controllers/SwapApiController.ts | 1 - .../w3m-convert-select-token-view/index.ts | 5 ++++- .../src/composites/wui-convert-input/index.ts | 1 - packages/ui/src/utils/Math.ts | 20 +++++++++++++------ 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/packages/core/src/controllers/SwapApiController.ts b/packages/core/src/controllers/SwapApiController.ts index cb79a57269..d5d286e7ff 100644 --- a/packages/core/src/controllers/SwapApiController.ts +++ b/packages/core/src/controllers/SwapApiController.ts @@ -501,7 +501,6 @@ export const SwapApiController = { const totalGasCostInEther = this.calculateGasPriceInEther(gas, gasPrice) const networkPriceNumber = parseFloat(state.networkPrice) const totalCostInUSD = totalGasCostInEther * networkPriceNumber - console.log('>>> networkPriceNumber', networkPriceNumber, totalGasCostInEther, totalCostInUSD) return totalCostInUSD }, diff --git a/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts b/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts index 633332238b..909bf449ef 100644 --- a/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts @@ -116,6 +116,7 @@ export class W3mConvertSelectTokenView extends LitElement { const selected = tokenInfo.symbol === this.sourceToken?.symbol || tokenInfo.symbol === this.toToken?.symbol + return html` { - if (!selected) this.onSelectToken(tokenInfo) + if (!selected) { + this.onSelectToken(tokenInfo) + } }} > diff --git a/packages/ui/src/composites/wui-convert-input/index.ts b/packages/ui/src/composites/wui-convert-input/index.ts index c8de0d60f7..04f944f7ad 100644 --- a/packages/ui/src/composites/wui-convert-input/index.ts +++ b/packages/ui/src/composites/wui-convert-input/index.ts @@ -73,7 +73,6 @@ export class WuiConvertInput extends LitElement { // -- Private ------------------------------------------- // private handleKeydown(event: KeyboardEvent) { - console.log('>>> keypress', event) const allowedKeys = [ 'Backspace', 'Meta', diff --git a/packages/ui/src/utils/Math.ts b/packages/ui/src/utils/Math.ts index 078401deb7..e9f5c615a2 100644 --- a/packages/ui/src/utils/Math.ts +++ b/packages/ui/src/utils/Math.ts @@ -6,13 +6,21 @@ * @returns */ export function interpolate(inputRange: number[], outputRange: number[], value: number) { - const originalRangeMin = inputRange[0] as number - const originalRangeMax = inputRange[1] as number - const newRangeMin = outputRange[0] as number - const newRangeMax = outputRange[1] as number + if (inputRange.length !== 2 || outputRange.length !== 2) { + throw new Error('inputRange and outputRange must be an array of length 2') + } - if (value < originalRangeMin) return newRangeMin - if (value > originalRangeMax) return newRangeMax + const originalRangeMin = inputRange[0] || 0 + const originalRangeMax = inputRange[1] || 0 + const newRangeMin = outputRange[0] || 0 + const newRangeMax = outputRange[1] || 0 + + if (value < originalRangeMin) { + return newRangeMin + } + if (value > originalRangeMax) { + return newRangeMax + } return ( ((newRangeMax - newRangeMin) / (originalRangeMax - originalRangeMin)) * From 554bc857ba58a7d1a88362adf3f33884f604333e Mon Sep 17 00:00:00 2001 From: enesozturk Date: Fri, 15 Mar 2024 10:49:36 +0300 Subject: [PATCH 25/96] refactor: separate convert controllers --- packages/core/index.ts | 7 +- .../src/controllers/ConnectionController.ts | 12 +- .../src/controllers/ConvertApiController.ts | 332 +++++++++ .../core/src/controllers/ConvertController.ts | 588 +++++++++++++++ .../core/src/controllers/SwapApiController.ts | 676 ------------------ packages/ethers/src/client.ts | 75 +- packages/ethers5/src/client.ts | 33 +- .../views/w3m-convert-preview-view/index.ts | 36 +- .../w3m-convert-select-token-view/index.ts | 48 +- .../src/views/w3m-convert-view/index.ts | 147 ++-- packages/wagmi/src/client.ts | 42 +- 11 files changed, 1177 insertions(+), 819 deletions(-) create mode 100644 packages/core/src/controllers/ConvertApiController.ts create mode 100644 packages/core/src/controllers/ConvertController.ts delete mode 100644 packages/core/src/controllers/SwapApiController.ts diff --git a/packages/core/index.ts b/packages/core/index.ts index a12584f159..1f63db62c1 100644 --- a/packages/core/index.ts +++ b/packages/core/index.ts @@ -55,8 +55,11 @@ export type { EventsControllerState } from './src/controllers/EventsController.j export { TransactionsController } from './src/controllers/TransactionsController.js' export type { TransactionsControllerState } from './src/controllers/TransactionsController.js' -export { SwapApiController } from './src/controllers/SwapApiController.js' -export type { SwapApiControllerState } from './src/controllers/SwapApiController.js' +export { ConvertApiController } from './src/controllers/ConvertApiController.js' +export type { ConvertApiControllerState } from './src/controllers/ConvertApiController.js' + +export { ConvertController } from './src/controllers/ConvertController.js' +export type { ConvertControllerState } from './src/controllers/ConvertController.js' // -- Utils ------------------------------------------------------------------- export { AssetUtil } from './src/utils/AssetUtil.js' diff --git a/packages/core/src/controllers/ConnectionController.ts b/packages/core/src/controllers/ConnectionController.ts index 50af024ff1..db3336d6ba 100644 --- a/packages/core/src/controllers/ConnectionController.ts +++ b/packages/core/src/controllers/ConnectionController.ts @@ -17,7 +17,9 @@ export interface ConnectionControllerClient { connectWalletConnect: (onUri: (uri: string) => void) => Promise disconnect: () => Promise signMessage: (message: string) => Promise - sendTransaction: (args: SendTransactionArgs) => Promise<`0x${string}`> + sendTransaction: (args: SendTransactionArgs) => Promise + getGasPrice: (chainId: number) => Promise + getEstimatedGas: (args: any) => Promise parseUnits: (value: string, decimals: number) => bigint formatUnits: (value: bigint, decimals: number) => string connectExternal?: (options: ConnectExternalOptions) => Promise @@ -98,6 +100,14 @@ export const ConnectionController = { return this._getClient().sendTransaction(args) }, + async getGasPrice(chainId: number) { + return this._getClient().getGasPrice(chainId) + }, + + async getEstimatedGas(args: any) { + return this._getClient().getEstimatedGas(args) + }, + checkInstalled(ids?: string[]) { return this._getClient().checkInstalled?.(ids) }, diff --git a/packages/core/src/controllers/ConvertApiController.ts b/packages/core/src/controllers/ConvertApiController.ts new file mode 100644 index 0000000000..a280f26b47 --- /dev/null +++ b/packages/core/src/controllers/ConvertApiController.ts @@ -0,0 +1,332 @@ +import { subscribeKey as subKey } from 'valtio/utils' +import { proxy, subscribe as sub } from 'valtio/vanilla' +import { CoreHelperUtil } from '../utils/CoreHelperUtil.js' +import { NetworkController } from './NetworkController.js' +import { FetchUtil } from '../utils/FetchUtil.js' +import { AccountController } from './AccountController.js' +import { ConnectionController } from './ConnectionController.js' + +const ONEINCH_API_BASE_URL = 'https://1inch-swap-proxy.walletconnect-v1-bridge.workers.dev' +const CURRENT_CHAIN_ADDRESS = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee' +export const DEFAULT_SLIPPAGE_TOLERANCE = '0.5' + +const OneInchAPIEndpoints = { + approveTransaction: (chainId: number) => `/swap/v5.2/${chainId}/approve/transaction`, + approveAllowance: (chainId: number) => `/swap/v5.2/${chainId}/approve/allowance`, + gas: (chainId: number) => `/gas-price/v1.5/${chainId}`, + gasPrice: (chainId: number) => `/gas-price/v1.5/${chainId}`, + swap: (chainId: number) => `/swap/v5.2/${chainId}/swap`, + tokens: (chainId: number) => `/swap/v5.2/${chainId}/tokens`, + tokensCustom: (chainId: number) => `/token/v1.2/${chainId}/custom`, + tokensPrices: (chainId: number) => `/price/v1.1/${chainId}`, + search: (chainId: number) => `/token/v1.2/${chainId}/search`, + balance: (chainId: number, address: string | undefined) => + `/balance/v1.2/${chainId}/balances/${address}`, + quote: (chainId: number) => `/swap/v6.0/${chainId}/quote` +} + +// -- Types --------------------------------------------- // +// #region Types +export interface ConvertApiControllerState {} + +export type TokenInfo = { + address: `0x${string}` + symbol: string + name: string + decimals: number + logoURI: string + domainVersion?: string + eip2612?: boolean + isFoT?: boolean + tags?: string[] +} + +export interface TokenInfoWithPrice extends TokenInfo { + price: string +} + +export interface TokenInfoWithBalance extends TokenInfo { + balance: string + price: string +} + +export type SwapApprovalData = { + data: `0x${string}` + to: `0x${string}` + gasPrice: string + value: string +} + +export type TokenList = { + tokens: Record +} + +export type GetAllowanceParams = { + fromAddress: string + sourceTokenAddress: string + sourceTokenAmount: string + sourceTokenDecimals: number +} + +export type GetApprovalParams = { + sourceTokenAddress: string + sourceTokenAmount?: string +} + +export type GetConvertDataParams = { + sourceTokenAddress: string + toTokenAddress: string + sourceTokenAmount: string + fromAddress: string + decimals: number +} + +export type TransactionData = { + from: string + to: `0x${string}` + data: `0x${string}` + value: string + gas: bigint + gasPrice: string +} + +export type GetConvertDataResponse = { + toAmount: string + tx: TransactionData +} + +type StateKey = keyof ConvertApiControllerState +// #endregion + +// -- State --------------------------------------------- // +const state = proxy({}) + +// -- Controller ---------------------------------------- // +export const ConvertApiController = { + state, + + subscribe(callback: (newState: ConvertApiControllerState) => void) { + return sub(state, () => callback(state)) + }, + + subscribeKey( + key: K, + callback: (value: ConvertApiControllerState[K]) => void + ) { + return subKey(state, key, callback) + }, + + get1InchAPI() { + const api = new FetchUtil({ baseUrl: ONEINCH_API_BASE_URL }) + const chainId = CoreHelperUtil.getEvmChainId(NetworkController.state.caipNetwork?.id) + const { address } = AccountController.state + + return { + api, + paths: { + approveTransaction: OneInchAPIEndpoints.approveTransaction(chainId), + approveAllowance: OneInchAPIEndpoints.approveAllowance(chainId), + gas: OneInchAPIEndpoints.gasPrice(chainId), + gasPrice: OneInchAPIEndpoints.gasPrice(chainId), + swap: OneInchAPIEndpoints.swap(chainId), + tokens: OneInchAPIEndpoints.tokens(chainId), + tokensCustom: OneInchAPIEndpoints.tokensCustom(chainId), + tokenPrices: OneInchAPIEndpoints.tokensPrices(chainId), + search: OneInchAPIEndpoints.search(chainId), + balance: OneInchAPIEndpoints.balance(chainId, address), + quote: OneInchAPIEndpoints.quote(chainId) + } + } + }, + + _getSwapParams() { + const { address } = AccountController.state + if (!address) { + throw new Error('No address found to swap the tokens from.') + } + + return { + fromAddress: address as `0x${string}`, + slippage: DEFAULT_SLIPPAGE_TOLERANCE + } + }, + + // /gas-price/v1.5/${chainId} + async getGasPrice() { + const { api, paths } = this.get1InchAPI() + + const gasPrices = await api.get>({ + path: paths.gasPrice, + headers: { 'content-type': 'application/json' } + }) + + return gasPrices + }, + + // - /swap/v5.2/${chainId}/approve/allowance + async checkConvertAllowance({ + fromAddress, + sourceTokenAddress, + sourceTokenAmount, + sourceTokenDecimals + }: GetAllowanceParams) { + const { api, paths } = this.get1InchAPI() + + const res = await api.get<{ allowance: string }>({ + path: paths.approveAllowance, + params: { tokenAddress: sourceTokenAddress, walletAddress: fromAddress } + }) + + if (res?.allowance && sourceTokenAmount && sourceTokenDecimals) { + const parsedValue = ConnectionController.parseUnits(sourceTokenAmount, sourceTokenDecimals) + const hasAllowance = BigInt(res.allowance) >= parsedValue + + return hasAllowance + } + + return false + }, + + // - /swap/v5.2/${chainId}/tokens + async getTokenList() { + const { api, paths } = this.get1InchAPI() + + return await api.get({ path: paths.tokens }) + }, + + // - /token/v1.2/${chainId}/search + async searchTokens(searchTerm: string) { + const { api, paths } = this.get1InchAPI() + + return await api.get({ + path: paths.search, + params: { query: searchTerm } + }) + }, + + async getMyTokensWithBalance() { + const { balances, tokenAddresses } = await this.getBalances() + + if (!tokenAddresses?.length) { + return undefined + } + + const addresses = [...tokenAddresses, CURRENT_CHAIN_ADDRESS] + + const [tokenInfos, tokensPrices] = await Promise.all([ + this.getTokenInfoWithAddresses(addresses), + this.getTokenPriceWithAddresses(addresses) + ]) + + const mergedTokensWithBalances = this.mergeTokensWithBalanceAndPrice( + tokenInfos, + balances, + tokensPrices + ) + + return mergedTokensWithBalances + }, + + // - /balance/v1.2/${chainId}/balances/${address} + async getBalances() { + const { api, paths } = this.get1InchAPI() + + const balances = await api.get>({ + path: paths.balance + }) + + const nonEmptyBalances = Object.entries(balances).reduce>( + (filteredBalances, [tokenAddress, balance]) => { + if (balance !== '0') { + filteredBalances[tokenAddress] = balance + } + + return filteredBalances + }, + {} + ) + + return { balances: nonEmptyBalances, tokenAddresses: Object.keys(nonEmptyBalances) } + }, + + // - /token/v1.2/${chainId}/custom + async getTokenInfoWithAddresses(addresses: string[]) { + const { api, paths } = this.get1InchAPI() + + return api.get>({ + path: paths.tokensCustom, + params: { addresses: addresses.join(',') } + }) + }, + + // - /price/v1.1/${chainId} + async getTokenPriceWithAddresses(addresses: string[]) { + const { api, paths } = this.get1InchAPI() + + return await api.post>({ + path: paths.tokenPrices, + body: { tokens: addresses, currency: 'USD' }, + headers: { + 'content-type': 'application/json' + } + }) + }, + + mergeTokensWithBalanceAndPrice( + tokens: Record, + balances: Record, + tokensPrice: Record + ) { + const mergedTokens = Object.entries(tokens).reduce>( + (mergedTokens, [tokenAddress, tokenInfo]) => { + mergedTokens[tokenAddress] = { + ...tokenInfo, + balance: ConnectionController.formatUnits( + BigInt(balances[tokenAddress] ?? '0'), + tokenInfo.decimals + ), + price: tokensPrice[tokenAddress] ?? '0' + } + + return mergedTokens + }, + {} + ) + + return mergedTokens + }, + + // --- /swap/v5.2/${chainId}/swap + async getConvertData({ + sourceTokenAddress, + toTokenAddress, + sourceTokenAmount, + fromAddress, + decimals = 9 + }: GetConvertDataParams): Promise { + const { api, paths } = this.get1InchAPI() + + return await api.get({ + path: paths.swap, + params: { + src: sourceTokenAddress, + dst: toTokenAddress, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + from: fromAddress, + amount: ConnectionController.parseUnits(sourceTokenAmount, decimals).toString() + } + }) + }, + + // --- /swap/v5.2/${chainId}/approve/transaction + async getConvertApprovalData({ sourceTokenAddress }: GetApprovalParams) { + const { api, paths } = this.get1InchAPI() + + return await api.get({ + path: paths.approveTransaction, + params: { + tokenAddress: sourceTokenAddress + } + }) + } +} diff --git a/packages/core/src/controllers/ConvertController.ts b/packages/core/src/controllers/ConvertController.ts new file mode 100644 index 0000000000..08e57a60bf --- /dev/null +++ b/packages/core/src/controllers/ConvertController.ts @@ -0,0 +1,588 @@ +import { subscribeKey as subKey } from 'valtio/utils' +import { proxy, subscribe as sub } from 'valtio/vanilla' +import { CoreHelperUtil } from '../utils/CoreHelperUtil.js' +import { NetworkController } from './NetworkController.js' +import { AccountController } from './AccountController.js' +import { ConstantsUtil } from '../utils/ConstantsUtil.js' +import { ConnectionController } from './ConnectionController.js' +import { ConvertApiController, type TransactionData } from './ConvertApiController.js' +import { SnackController } from './SnackController.js' + +const CURRENT_CHAIN_ADDRESS = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee' +export const DEFAULT_SLIPPAGE_TOLERANCE = '0.5' + +// -- Types --------------------------------------------- // +type PriceDifferenceDirection = 'up' | 'down' | 'none' + +export interface ConvertControllerState { + // Loading states + initialized: boolean + loadingPrices: boolean + loading?: boolean + + // Approval & Convert transaction states + approvalTransaction: object | undefined + convertLoading: boolean + convertTransaction?: TransactionData + transactionLoading?: boolean + transactionError?: string + + // Input values + sourceToken?: TokenInfo + sourceTokenAmount: string + sourceTokenPriceInUSD: number + toToken?: TokenInfo + toTokenAmount: string + toTokenPriceInUSD: number + networkPrice: string + networkAddress: string | undefined + inputError: string | undefined + + // Request values + slippage: string + + // Tokens + tokens?: Record + popularTokens?: Record + foundTokens?: TokenInfo[] + myTokensWithBalance?: Record + tokensPriceMap: Record + + // Calculations + gasPriceInUSD?: number + priceImpact: number | undefined + maxSlippage: number | undefined +} + +export interface TokenInfo { + address: `0x${string}` + symbol: string + name: string + decimals: number + logoURI: string + domainVersion?: string + eip2612?: boolean + isFoT?: boolean + tags?: string[] +} + +export interface TokenInfoWithPrice extends TokenInfo { + price: string +} + +export interface TokenInfoWithBalance extends TokenInfo { + balance: string + price: string +} + +type StateKey = keyof ConvertControllerState + +// -- State --------------------------------------------- // +const state = proxy({ + // Loading states + initialized: false, + loading: false, + loadingPrices: false, + + // Approval & Convert transaction states + approvalTransaction: undefined, + convertLoading: false, + convertTransaction: undefined, + transactionError: undefined, + transactionLoading: false, + + // Input values + sourceToken: undefined, + sourceTokenAmount: '', + sourceTokenPriceInUSD: 0, + toToken: undefined, + toTokenAmount: '', + toTokenPriceInUSD: 0, + networkPrice: '0', + networkAddress: CURRENT_CHAIN_ADDRESS, + inputError: undefined, + + // Request values + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + + // Tokens + tokens: undefined, + popularTokens: undefined, + foundTokens: undefined, + myTokensWithBalance: undefined, + tokensPriceMap: {}, + + // Calculations + gasPriceInUSD: 0, + priceImpact: undefined, + maxSlippage: undefined +}) + +// -- Controller ---------------------------------------- // +export const ConvertController = { + state, + + subscribe(callback: (newState: ConvertControllerState) => void) { + return sub(state, () => callback(state)) + }, + + subscribeKey(key: K, callback: (value: ConvertControllerState[K]) => void) { + return subKey(state, key, callback) + }, + + getParams() { + const { address } = AccountController.state + + if (!address) { + throw new Error('No address found to swap the tokens from.') + } + + return { + fromAddress: address as `0x${string}`, + sourceTokenAddress: state.sourceToken?.address, + toTokenAddress: state.toToken?.address, + toTokenAmount: state.toTokenAmount, + toTokenDecimals: state.toToken?.decimals, + sourceTokenAmount: state.sourceTokenAmount, + sourceTokenDecimals: state.sourceToken?.decimals + } + }, + + setSourceToken(sourceToken: TokenInfo | undefined) { + if (!sourceToken) { + return + } + + state.sourceToken = sourceToken + if (sourceToken?.address && !state.tokensPriceMap[sourceToken.address]) { + this.setTokenPriceToMap(sourceToken.address) + } + }, + + setSourceTokenAmount(amount: string) { + const { sourceTokenAddress } = this.getParams() + + state.sourceTokenAmount = amount + + if (!sourceTokenAddress) { + throw new Error('No source token address found') + } + + this.checkSourceTokenValues(sourceTokenAddress, amount) + }, + + async checkSourceTokenValues(address: string, amount: string) { + const balance = state.myTokensWithBalance?.[address]?.balance || '0' + const price = await this.getAddressPrice(address) + + const balancePriceInUSD = parseFloat(balance) * price + const amountPriceInUSD = parseFloat(amount) * price + const insufficientBalance = balancePriceInUSD < amountPriceInUSD + + state.sourceTokenPriceInUSD = price + state.inputError = insufficientBalance ? 'Insufficient balance' : undefined + if (insufficientBalance) { + state.toTokenAmount = '0' + state.toTokenPriceInUSD = 0 + } + }, + + setToTokenAmount(amount: string) { + state.toTokenAmount = amount + }, + + setToToken(toToken: TokenInfo | undefined) { + if (!toToken) { + return + } + + state.toToken = toToken + if (toToken.address && !state.tokensPriceMap[toToken.address]) { + this.setTokenPriceToMap(toToken.address) + } + }, + + setLoading(isLoading: boolean) { + state.loading = isLoading + }, + + switchTokens() { + const newSourceToken = state.toToken ? Object.assign({}, state.toToken) : undefined + const newToToken = state.sourceToken ? Object.assign({}, state.sourceToken) : undefined + + this.setSourceToken(newSourceToken) + this.setToToken(newToToken) + + state.sourceTokenAmount = '' + state.toTokenAmount = '' + }, + + clearFoundTokens() { + state.foundTokens = undefined + }, + + clearTokens() { + state.tokens = undefined + }, + + resetState() { + state.sourceToken = undefined + state.toToken = undefined + state.sourceTokenAmount = '0' + state.toTokenAmount = '0' + state.sourceTokenPriceInUSD = 0 + state.toTokenPriceInUSD = 0 + state.gasPriceInUSD = 0 + }, + + clearMyTokens() { + state.myTokensWithBalance = undefined + }, + + clearError() { + state.transactionError = undefined + }, + + async initializeState() { + if (!state.initialized) { + await this.getTokenList() + await this.getNetworkTokenPrice() + await this.getMyTokensWithBalance() + state.initialized = true + } + }, + + calculatePriceDifference() { + const sourceTokenValue = parseFloat(state.sourceTokenAmount) * state.sourceTokenPriceInUSD + const toTokenValue = parseFloat(state.toTokenAmount) * state.toTokenPriceInUSD + + if (!sourceTokenValue || !toTokenValue) { + return { + percentage: 0, + direction: 'none' as PriceDifferenceDirection + } + } + + const priceChange = sourceTokenValue - toTokenValue + console.log( + '>>> calculatePriceDifference', + priceChange, + toTokenValue, + sourceTokenValue, + state.sourceTokenAmount, + state.sourceTokenPriceInUSD, + state.toTokenAmount, + state.toTokenPriceInUSD + ) + const changeInPercentage = (priceChange / toTokenValue) * 100 + let direction: PriceDifferenceDirection = 'none' + + if (changeInPercentage > 0) { + direction = 'up' + } else if (changeInPercentage < 0) { + direction = 'down' + } + + return { + percentage: changeInPercentage, + direction + } + }, + + async getTokenList() { + const res = await ConvertApiController.getTokenList() + + state.tokens = res.tokens + state.popularTokens = Object.entries(res.tokens) + .sort(([, aTokenInfo], [, bTokenInfo]) => { + if (aTokenInfo.symbol < bTokenInfo.symbol) { + return -1 + } + if (aTokenInfo.symbol > bTokenInfo.symbol) { + return 1 + } + + return 0 + }) + .reduce>((limitedTokens, [tokenAddress, tokenInfo]) => { + if (ConstantsUtil.POPULAR_TOKENS.includes(tokenInfo.symbol)) { + limitedTokens[tokenAddress] = tokenInfo + } + + return limitedTokens + }, {}) + + return state.tokens + }, + + async getAddressPrice(address: string) { + const existPrice = state.tokensPriceMap[address] + if (existPrice) { + return parseFloat(existPrice) + } + const prices = await ConvertApiController.getTokenPriceWithAddresses([address]) + const price = prices[address] || '0' + state.tokensPriceMap[address] = price + return parseFloat(price) + }, + + async setTokenPriceToMap(address: string) { + const prices = await ConvertApiController.getTokenPriceWithAddresses([address]) + const price = prices[address] || '0' + state.tokensPriceMap[address] = price + if (address === CURRENT_CHAIN_ADDRESS) { + state.networkPrice = price + } + }, + + async getNetworkTokenPrice() { + const prices = await ConvertApiController.getTokenPriceWithAddresses([CURRENT_CHAIN_ADDRESS]) + const price = prices[CURRENT_CHAIN_ADDRESS] || '0' + state.tokensPriceMap[CURRENT_CHAIN_ADDRESS] = price + state.networkPrice = price + }, + + async getMyTokensWithBalance() { + const res = await ConvertApiController.getMyTokensWithBalance() + if (!res) { + return + } + state.tokensPriceMap = Object.entries(res).reduce>( + (prices, [tokenAddress, tokenInfo]) => { + prices[tokenAddress] = tokenInfo.price + return prices + }, + {} + ) + state.myTokensWithBalance = res + }, + + async searchTokens(searchTerm: string) { + const response = await ConvertApiController.searchTokens(searchTerm) + }, + + calculateGasPriceInEther(gas: number, gasPrice: string) { + const gasPriceNumber = BigInt(gasPrice) + const totalGasCostInWei = gasPriceNumber * BigInt(gas) + const totalGasCostInEther = Number(totalGasCostInWei) / 1e18 + + return totalGasCostInEther + }, + + calculateGasPriceInUSD(gas: number, gasPrice: string) { + try { + const totalGasCostInEther = this.calculateGasPriceInEther(gas, gasPrice) + const networkPriceNumber = parseFloat(state.networkPrice) + const totalCostInUSD = totalGasCostInEther * networkPriceNumber + + return totalCostInUSD + } catch (error) { + return 0 + } + }, + + calculatePriceImpact(_toTokenAmount: string) { + const sourceTokenAmount = parseFloat(state.sourceTokenAmount) + const toTokenAmount = parseFloat(_toTokenAmount) + const sourceTokenPrice = state.sourceTokenPriceInUSD + const toTokenPrice = state.toTokenPriceInUSD + + const effectivePrice = (sourceTokenAmount * sourceTokenPrice) / toTokenAmount + const priceImpact = ((effectivePrice - toTokenPrice) / toTokenPrice) * 100 + + return priceImpact + }, + + calculateMaxSlippage() { + const fromTokenAmount = parseFloat(state.sourceTokenAmount) + const slippageToleranceDecimal = parseFloat(state.slippage) / 100 + const maxSlippageAmount = fromTokenAmount * slippageToleranceDecimal + + return maxSlippageAmount + }, + + // 1. Call convert token when amount is set + async convertTokens() { + const { sourceTokenAddress, toTokenAddress } = this.getParams() + + if (!sourceTokenAddress || !toTokenAddress) { + return + } + + state.convertLoading = true + await this.makeChecks() + }, + + // 2. Make checks for allowance and convert if needed + async makeChecks() { + const { + fromAddress, + sourceTokenAddress, + sourceTokenAmount, + sourceTokenDecimals, + toTokenDecimals, + toTokenAddress + } = this.getParams() + + if ( + !sourceTokenAddress || + !sourceTokenAmount || + parseFloat(sourceTokenAmount) === 0 || + !sourceTokenDecimals || + !toTokenDecimals || + !toTokenAddress + ) { + return + } + + state.loading = true + const hasAllowance = await ConvertApiController.checkConvertAllowance({ + fromAddress, + sourceTokenAddress, + sourceTokenAmount, + sourceTokenDecimals + }) + + if (!hasAllowance) { + const transaction = await this.createTokenAllowance() + state.approvalTransaction = transaction + } else { + state.approvalTransaction = undefined + const response = await this.createConvert() + + if (response.tx) { + state.convertTransaction = response.tx + this.setToTokenAmountValue(response.toAmount, toTokenDecimals, toTokenAddress) + this.setTransactionDetails(response.tx.gas, response.tx.gasPrice) + } + } + + state.loading = false + }, + + // 3.1.1 Create token allowance + async createTokenAllowance() { + const { fromAddress, sourceTokenAddress } = this.getParams() + + if (!sourceTokenAddress) { + throw new Error('>>> createTokenAllowance - No source token address found.') + } + + state.loading = true + const transaction = await ConvertApiController.getConvertApprovalData({ + sourceTokenAddress + }) + + const gasLimit = await ConnectionController.getEstimatedGas({ + ...transaction, + from: fromAddress + }) + + return { + ...transaction, + gas: gasLimit + } + }, + + // 3.1.2 Request approval for token + async sendTransactionForApproval(data) { + const { fromAddress } = this.getParams() + state.transactionLoading = true + + try { + await ConnectionController.sendTransaction({ + address: fromAddress, + ...data + }) + + state.approvalTransaction = undefined + state.transactionLoading = true + } catch (error) { + state.transactionError = error?.shortMessage + state.transactionLoading = false + SnackController.showError(error?.shortMessage || 'Transaction errror') + } + }, + + // 3.2.1 Create convert + async createConvert() { + const { + fromAddress, + sourceTokenAddress, + sourceTokenDecimals, + sourceTokenAmount, + toTokenAddress + } = this.getParams() + + if (!sourceTokenAmount || !sourceTokenAddress || !toTokenAddress || !sourceTokenDecimals) { + throw new Error('>>> createConvert: Invalid values to start converting') + } + + return await ConvertApiController.getConvertData({ + fromAddress, + sourceTokenAddress, + sourceTokenAmount, + toTokenAddress, + decimals: sourceTokenDecimals + }) + }, + + // 3.2.2 Send convert transaction + async sendTransactionForConvert(data) { + const { fromAddress } = this.getParams() + + state.transactionLoading = true + try { + const transactionHash = await ConnectionController.sendTransaction({ + address: fromAddress, + to: data.to, + data: data.data, + gas: data.gas, + gasPrice: BigInt(data.gasPrice), + value: BigInt(data.value), + chainId: CoreHelperUtil.getEvmChainId(NetworkController.state.caipNetwork?.id) + }) + return transactionHash + } catch (error) { + state.transactionError = error?.shortMessage + state.transactionLoading = false + SnackController.showError(error?.shortMessage || 'Transaction errror') + return undefined + } + }, + + async setToTokenAmountValue( + toTokenAmount: string, + toTokenDecimals: number, + toTokenAddress: string + ) { + if (!toTokenAddress || !toTokenAmount) { + return + } + + const amount = ConnectionController.formatUnits(BigInt(toTokenAmount), toTokenDecimals) + const sourceTokenAmount = parseFloat(state.sourceTokenAmount) + const sourceTokenPrice = state.sourceTokenPriceInUSD + const toTokenPrice = parseFloat(state.tokensPriceMap[toTokenAddress] || '0') + const toTokenAmountValue = (sourceTokenAmount * sourceTokenPrice) / toTokenPrice + + state.toTokenAmount = amount + state.toTokenPriceInUSD = toTokenPrice + state.toTokenAmount = toTokenAmountValue.toString() + }, + + async setTransactionDetails(gas: bigint, gasPrice: string) { + const { toTokenAddress, toTokenAmount } = this.getParams() + + if (!toTokenAddress) { + return + } + + const toTokenPrice = state.tokensPriceMap[toTokenAddress] || '0' + state.toTokenPriceInUSD = parseFloat(toTokenPrice) + + state.gasPriceInUSD = this.calculateGasPriceInUSD(Number(gas), gasPrice) + state.priceImpact = this.calculatePriceImpact(toTokenAmount) + state.maxSlippage = this.calculateMaxSlippage() + } +} diff --git a/packages/core/src/controllers/SwapApiController.ts b/packages/core/src/controllers/SwapApiController.ts deleted file mode 100644 index d5d286e7ff..0000000000 --- a/packages/core/src/controllers/SwapApiController.ts +++ /dev/null @@ -1,676 +0,0 @@ -import { subscribeKey as subKey } from 'valtio/utils' -import { proxy, subscribe as sub } from 'valtio/vanilla' -import { CoreHelperUtil } from '../utils/CoreHelperUtil.js' -import { NetworkController } from './NetworkController.js' -import { FetchUtil } from '../utils/FetchUtil.js' -import { AccountController } from './AccountController.js' -import { ConstantsUtil } from '../utils/ConstantsUtil.js' -import { ConnectionController } from './ConnectionController.js' -import { RouterController } from './RouterController.js' - -const ONEINCH_API_BASE_URL = 'https://1inch-swap-proxy.walletconnect-v1-bridge.workers.dev' -const CURRENT_CHAIN_ADDRESS = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee' -export const DEFAULT_SLIPPAGE_TOLERANCE = 0.5 - -const OneInchAPIEndpoints = { - approveTransaction: (chainId: number) => `/swap/v5.2/${chainId}/approve/transaction`, - approveAllowance: (chainId: number) => `/swap/v5.2/${chainId}/approve/allowance`, - gasPrice: (chainId: number) => `/gas-price/v1.5/${chainId}`, - swap: (chainId: number) => `/swap/v5.2/${chainId}/swap`, - tokens: (chainId: number) => `/swap/v5.2/${chainId}/tokens`, - tokensCustom: (chainId: number) => `/token/v1.2/${chainId}/custom`, - tokensPrices: (chainId: number) => `/price/v1.1/${chainId}`, - search: (chainId: number) => `/token/v1.2/${chainId}/search`, - balance: (chainId: number, address: string | undefined) => - `/balance/v1.2/${chainId}/balances/${address}` -} - -// -- Types --------------------------------------------- // -type PriceDifferenceDirection = 'up' | 'down' | 'none' - -export interface SwapApiControllerState { - initialLoading?: boolean - isTransactionPending?: boolean - sourceToken?: TokenInfo - sourceTokenAmount: string - sourceTokenPriceInUSD: number - toToken?: TokenInfo - toTokenAmount: string - toTokenPriceInUSD: number - networkPrice: string - networkAddress: string | undefined - gasPriceInUSD?: number - swapTransaction?: TransactionData - swapApproval?: SwapApprovalData - slippage: number - disableEstimate?: boolean - allowPartialFill?: boolean - hasAllowance: boolean - loading?: boolean - tokens?: Record - popularTokens?: Record - foundTokens?: TokenInfo[] - myTokensWithBalance?: Record - tokensPriceMap: Record - swapErrorMessage?: string - loadingPrices: boolean - priceImpact: number | undefined - maxSlippage: number | undefined -} - -export interface TokenInfo { - address: `0x${string}` - symbol: string - name: string - decimals: number - logoURI: string - domainVersion?: string - eip2612?: boolean - isFoT?: boolean - tags?: string[] -} - -export interface TokenInfoWithPrice extends TokenInfo { - price: string -} - -export interface TokenInfoWithBalance extends TokenInfo { - balance: string - price: string -} - -interface TransactionData { - from: string - to: `0x${string}` - data: `0x${string}` - value: string - gas: number - gasPrice: string -} - -interface SwapResponse { - toAmount: string - tx: TransactionData -} - -interface SwapApprovalData { - data: `0x${string}` - to: `0x${string}` - gasPrice: string - value: string -} - -interface TokenList { - tokens: Record -} - -interface SwapRequestError { - error: string - description: string - statusCode: 400 | 500 - requestId: string - meta: HttpExceptionMeta[] -} - -interface HttpExceptionMeta { - type: string - value: string -} - -type StateKey = keyof SwapApiControllerState - -// -- State --------------------------------------------- // -const state = proxy({ - hasAllowance: false, - toTokenAmount: '', - sourceTokenAmount: '', - sourceTokenPriceInUSD: 0, - toTokenPriceInUSD: 0, - networkPrice: '0', - networkAddress: CURRENT_CHAIN_ADDRESS, - gasPriceInUSD: 0, - slippage: DEFAULT_SLIPPAGE_TOLERANCE, - disableEstimate: false, - allowPartialFill: false, - initialLoading: false, - loading: false, - tokens: undefined, - popularTokens: undefined, - foundTokens: undefined, - myTokensWithBalance: undefined, - tokensPriceMap: {}, - swapErrorMessage: undefined, - isTransactionPending: false, - loadingPrices: false, - swapTransaction: undefined, - priceImpact: undefined, - maxSlippage: undefined -}) - -// -- Controller ---------------------------------------- // -export const SwapApiController = { - state, - - subscribe(callback: (newState: SwapApiControllerState) => void) { - return sub(state, () => callback(state)) - }, - - subscribeKey(key: K, callback: (value: SwapApiControllerState[K]) => void) { - return subKey(state, key, callback) - }, - - _get1inchApi() { - const api = new FetchUtil({ baseUrl: ONEINCH_API_BASE_URL }) - const chainId = CoreHelperUtil.getEvmChainId(NetworkController.state.caipNetwork?.id) - const { address } = AccountController.state - - return { - api, - paths: { - approveTransaction: OneInchAPIEndpoints.approveTransaction(chainId), - approveAllowance: OneInchAPIEndpoints.approveAllowance(chainId), - gasPrice: OneInchAPIEndpoints.gasPrice(chainId), - swap: OneInchAPIEndpoints.swap(chainId), - tokens: OneInchAPIEndpoints.tokens(chainId), - tokensCustom: OneInchAPIEndpoints.tokensCustom(chainId), - tokenPrices: OneInchAPIEndpoints.tokensPrices(chainId), - search: OneInchAPIEndpoints.search(chainId), - balance: OneInchAPIEndpoints.balance(chainId, address) - } - } - }, - - _getSwapParams() { - const { address } = AccountController.state - if (!address) { - throw new Error('No address found to swap the tokens from.') - } - - return { - fromAddress: address as `0x${string}`, - sourceTokenAddress: state.sourceToken?.address, - toTokenAddress: state.toToken?.address, - sourceTokenAmount: state.sourceTokenAmount, - slippage: state.slippage?.toString() - } - }, - - setSourceToken(sourceToken?: TokenInfo) { - state.sourceToken = sourceToken - state.sourceTokenAmount = '' - state.toTokenAmount = '' - if (sourceToken?.address && !state.tokensPriceMap[sourceToken?.address]) { - this.getTokenPriceWithAddresses([sourceToken?.address]) - } - }, - - setSourceTokenAmount(amount: string) { - state.sourceTokenAmount = amount - const price = state.tokensPriceMap[state.sourceToken?.address ?? ''] - state.sourceTokenPriceInUSD = price ? parseFloat(price) : 0 - }, - - setToTokenAmount(amount: string) { - state.toTokenAmount = amount - }, - - setToToken(toToken?: TokenInfo) { - state.toToken = toToken - if (toToken?.address && !state.tokensPriceMap[toToken?.address]) { - this.getTokenPriceWithAddresses([toToken.address]) - } - }, - - setSlippage(slippage: number) { - state.slippage = slippage - }, - - setLoading(isLoading: boolean) { - state.loading = isLoading - }, - - switchTokens() { - const newSourceToken = state.toToken - const newToToken = state.sourceToken - - this.setSourceToken(newSourceToken) - this.setToToken(newToToken) - - state.sourceTokenAmount = '' - state.toTokenAmount = '' - }, - - clearFoundTokens() { - state.foundTokens = undefined - }, - - clearTokens() { - state.tokens = undefined - }, - - resetState() { - state.tokens = undefined - state.myTokensWithBalance = undefined - state.sourceToken = undefined - state.toToken = undefined - state.sourceTokenAmount = '0' - state.toTokenAmount = '0' - state.sourceTokenPriceInUSD = 0 - state.toTokenPriceInUSD = 0 - state.gasPriceInUSD = 0 - }, - - clearMyTokens() { - state.myTokensWithBalance = undefined - }, - - clearError() { - state.swapErrorMessage = undefined - }, - - async getGasPrice() { - const { api, paths } = this._get1inchApi() - - const gasPrices = await api.get>({ - path: paths.gasPrice, - headers: { 'content-type': 'application/json' } - }) - - return gasPrices - }, - - calculatePriceDifference() { - const sourceTokenValue = parseFloat(state.sourceTokenAmount) * state.sourceTokenPriceInUSD - const toTokenValue = parseFloat(state.toTokenAmount) * state.toTokenPriceInUSD - - if (!sourceTokenValue || !toTokenValue) { - return { - percentage: 0, - direction: 'none' as PriceDifferenceDirection - } - } - - const priceChange = sourceTokenValue - toTokenValue - const changeInPercentage = (priceChange / toTokenValue) * 100 - let direction: PriceDifferenceDirection = 'none' - - if (changeInPercentage > 0) { - direction = 'up' - } else if (changeInPercentage < 0) { - direction = 'down' - } - - return { - percentage: changeInPercentage, - direction - } - }, - - async getTokenAllowance() { - const { api, paths } = this._get1inchApi() - const { sourceTokenAddress, fromAddress, sourceTokenAmount } = this._getSwapParams() - - const res = await api.get<{ allowance: string }>({ - path: paths.approveAllowance, - params: { tokenAddress: sourceTokenAddress, walletAddress: fromAddress } - }) - - if (res?.allowance && sourceTokenAmount && state.sourceToken?.decimals) { - const hasAllowance = - BigInt(res.allowance) >= - ConnectionController.parseUnits(sourceTokenAmount, state.sourceToken?.decimals) - state.hasAllowance = hasAllowance - - return hasAllowance - } - - state.hasAllowance = false - - return false - }, - - async getTokenList(options?: { forceRefetch?: boolean }) { - if (state.tokens && !options?.forceRefetch) { - return state.tokens - } - const networkTokenSymbol = AccountController.state.balanceSymbol - - const { api, paths } = this._get1inchApi() - state.initialLoading = true - const res = await api.get({ path: paths.tokens }) - - state.tokens = res.tokens - state.networkAddress = - Object.keys(res.tokens).find(address => res.tokens[address]?.symbol === networkTokenSymbol) || - undefined - - state.popularTokens = Object.entries(res.tokens) - .sort(([, aTokenInfo], [, bTokenInfo]) => { - if (aTokenInfo.symbol < bTokenInfo.symbol) { - return -1 - } - if (aTokenInfo.symbol > bTokenInfo.symbol) { - return 1 - } - - return 0 - }) - .reduce>((limitedTokens, [tokenAddress, tokenInfo]) => { - if (ConstantsUtil.POPULAR_TOKENS.includes(tokenInfo.symbol)) { - limitedTokens[tokenAddress] = tokenInfo - } - - return limitedTokens - }, {}) - - await this.getMyTokensWithBalance({ forceRefetch: true }) - - state.initialLoading = false - - return state.tokens - }, - - async searchTokens(searchTerm: string) { - const { api, paths } = this._get1inchApi() - - const res = await api.get({ - path: paths.search, - params: { query: searchTerm } - }) - - state.foundTokens = res - }, - - async getMyTokensWithBalance(options?: { forceRefetch?: boolean }) { - if (!options?.forceRefetch) { - return state.myTokensWithBalance - } - - const { balances, tokenAddresses } = await this.getBalances() - - state.initialLoading = true - - if (!tokenAddresses?.length) { - state.initialLoading = false - - return undefined - } - - const addresses = state.networkAddress - ? [...tokenAddresses, state.networkAddress] - : tokenAddresses - - const [tokens, tokensPrice] = await Promise.all([ - this.getTokenInfoWithAddresses(addresses), - this.getTokenPriceWithAddresses(addresses) - ]) - - const mergedTokensWithBalances = this.mergeTokenWithBalances(tokens, balances, tokensPrice) - - state.myTokensWithBalance = mergedTokensWithBalances - state.initialLoading = false - - return mergedTokensWithBalances - }, - - async getBalances() { - const { api, paths } = this._get1inchApi() - - const balances = await api.get>({ - path: paths.balance - }) - - const nonEmptyBalances = Object.entries(balances).reduce>( - (filteredBalances, [tokenAddress, balance]) => { - if (balance !== '0') { - filteredBalances[tokenAddress] = balance - } - - return filteredBalances - }, - {} - ) - - return { balances: nonEmptyBalances, tokenAddresses: Object.keys(nonEmptyBalances) } - }, - - async getTokenInfoWithAddresses(addresses: string[]) { - const { api, paths } = this._get1inchApi() - - return api.get>({ - path: paths.tokensCustom, - params: { addresses: addresses.join(',') } - }) - }, - - async getTokenPriceWithAddresses(addresses: string[]) { - state.loadingPrices = true - - const { api, paths } = this._get1inchApi() - - const prices = await api.post>({ - path: paths.tokenPrices, - body: { tokens: addresses, currency: 'USD' }, - headers: { - 'content-type': 'application/json' - } - }) - - state.networkPrice = state.networkAddress ? prices[state.networkAddress] || '0' : '0' - - Object.entries(prices).forEach(([tokenAddress, price]) => { - state.tokensPriceMap[tokenAddress] = price - }) - - state.loadingPrices = false - - return prices - }, - - mergeTokenWithBalances( - tokens: Record, - balances: Record, - tokensPrice: Record - ) { - return Object.entries(tokens).reduce>( - (mergedTokens, [tokenAddress, tokenInfo], i) => { - mergedTokens[tokenAddress] = { - ...tokenInfo, - balance: balances[tokenAddress] ?? '0', - price: tokensPrice[tokenAddress] ?? '0' - } - if (i === 0) { - this.setSourceToken(tokenInfo) - } - - return mergedTokens - }, - {} - ) - }, - - calculateGasPriceInEther(gas: number, gasPrice: string) { - const gasPriceNumber = BigInt(gasPrice) - const totalGasCostInWei = gasPriceNumber * BigInt(gas) - const totalGasCostInEther = Number(totalGasCostInWei) / 1e18 - - return totalGasCostInEther - }, - - calculateGasPriceInUSD(gas: number, gasPrice: string) { - const totalGasCostInEther = this.calculateGasPriceInEther(gas, gasPrice) - const networkPriceNumber = parseFloat(state.networkPrice) - const totalCostInUSD = totalGasCostInEther * networkPriceNumber - - return totalCostInUSD - }, - - calculatePriceImpact(_toTokenAmount: string) { - const sourceTokenAmount = parseFloat(state.sourceTokenAmount) - const toTokenAmount = parseFloat(_toTokenAmount) - const sourceTokenPrice = state.sourceTokenPriceInUSD - const toTokenPrice = state.toTokenPriceInUSD - - const effectivePrice = (sourceTokenAmount * sourceTokenPrice) / toTokenAmount - const priceImpact = ((effectivePrice - toTokenPrice) / toTokenPrice) * 100 - - return priceImpact - }, - - calculateMaxSlippage() { - const fromTokenAmount = parseFloat(state.sourceTokenAmount) - const slippageToleranceDecimal = state.slippage / 100 - const maxSlippageAmount = fromTokenAmount * slippageToleranceDecimal - - return maxSlippageAmount - }, - - async getTokenSwapInfo() { - if (state.sourceToken?.address && state.toToken?.address) { - state.loading = true - try { - const hasAllowance = await SwapApiController.getTokenAllowance() - - if (hasAllowance) { - await SwapApiController.getSwapCalldata() - } else { - await SwapApiController.getSwapApprovalCalldata() - } - } catch (error) { - if (error instanceof Error) { - state.swapErrorMessage = error.message - } - } finally { - state.loading = false - } - } - }, - - async getSwapCalldata() { - const { api, paths } = this._get1inchApi() - const { fromAddress, slippage, sourceTokenAddress, sourceTokenAmount, toTokenAddress } = - this._getSwapParams() - - if (!sourceTokenAmount || !state.sourceToken?.address || !state.toToken?.address) { - return - } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const swapTransactionRes = await api.get({ - path: paths.swap, - params: { - src: sourceTokenAddress, - dst: toTokenAddress, - slippage, - from: fromAddress, - amount: ConnectionController.parseUnits( - sourceTokenAmount, - state.sourceToken?.decimals - ).toString() - } - }) - - if (swapTransactionRes.error) { - state.toTokenAmount = '' - state.swapTransaction = undefined - const swapTransaction: SwapRequestError = swapTransactionRes - state.swapErrorMessage = swapTransaction.description - } else { - state.swapErrorMessage = undefined - - const swapTransaction: SwapResponse = swapTransactionRes - state.swapTransaction = swapTransaction.tx - const transaction = swapTransaction.tx - - state.toTokenAmount = ConnectionController.formatUnits( - BigInt(swapTransaction.toAmount), - state.toToken.decimals - ) - const toTokenPrice = state.tokensPriceMap[state.toToken.address] || '0' - state.toTokenPriceInUSD = parseFloat(toTokenPrice) - - state.priceImpact = this.calculatePriceImpact(state.toTokenAmount) - state.maxSlippage = this.calculateMaxSlippage() - state.gasPriceInUSD = this.calculateGasPriceInUSD(transaction.gas, transaction.gasPrice) - } - }, - - async getSwapApprovalCalldata() { - const { api, paths } = this._get1inchApi() - const { sourceTokenAddress, sourceTokenAmount } = this._getSwapParams() - - if (!sourceTokenAmount || !state.sourceToken?.decimals) { - return - } - - const res = await api.get({ - path: paths.approveTransaction, - params: { - tokenAddress: sourceTokenAddress, - amount: ConnectionController.parseUnits( - sourceTokenAmount, - state.sourceToken.decimals - ).toString() - } - }) - - state.swapApproval = res - }, - - async swapTokens() { - state.isTransactionPending = true - const chainId = CoreHelperUtil.getEvmChainId(NetworkController.state.caipNetwork?.id) - - const { fromAddress } = this._getSwapParams() - - if (state.swapTransaction) { - try { - await ConnectionController.sendTransaction({ - address: fromAddress, - chainId, - data: state.swapTransaction.data, - to: state.swapTransaction.to, - gas: BigInt(state.swapTransaction.gas), - gasPrice: BigInt(state.swapTransaction.gasPrice), - value: BigInt(state.swapTransaction.value) - }) - - this.resetState() - RouterController.replace('Transactions') - - await this.getMyTokensWithBalance({ forceRefetch: true }) - } catch (error: unknown) { - if (error instanceof Error) { - state.swapErrorMessage = error.message - } - } finally { - state.isTransactionPending = false - } - } - }, - - async approveSwapTokens() { - const chainId = CoreHelperUtil.getEvmChainId(NetworkController.state.caipNetwork?.id) - - const { fromAddress } = this._getSwapParams() - - if (state.swapApproval) { - const { data, to, gasPrice, value } = state.swapApproval - - try { - await ConnectionController.sendTransaction({ - address: fromAddress, - chainId, - data, - to, - gasPrice: BigInt(gasPrice), - value: BigInt(value) - }) - state.hasAllowance = true - } catch (error: unknown) { - if (error instanceof Error) { - state.swapErrorMessage = error.message - } - } - } - } -} diff --git a/packages/ethers/src/client.ts b/packages/ethers/src/client.ts index 28fb539015..bc5cac4f41 100644 --- a/packages/ethers/src/client.ts +++ b/packages/ethers/src/client.ts @@ -8,9 +8,10 @@ import type { LibraryOptions, NetworkControllerClient, PublicStateControllerState, + SendTransactionArgs, Token } from '@web3modal/scaffold' -import { Web3ModalScaffold } from '@web3modal/scaffold' +import { AccountController, Web3ModalScaffold } from '@web3modal/scaffold' import { ConstantsUtil, PresetsUtil, HelpersUtil } from '@web3modal/scaffold-utils' import EthereumProvider from '@walletconnect/ethereum-provider' import type { Web3ModalSIWEClient } from '@web3modal/siwe' @@ -26,7 +27,10 @@ import { formatEther, JsonRpcProvider, InfuraProvider, - getAddress as getOriginalAddress + getAddress as getOriginalAddress, + parseUnits, + formatUnits, + parseEther } from 'ethers' import { EthersConstantsUtil, @@ -37,6 +41,9 @@ import type { EthereumProviderOptions } from '@walletconnect/ethereum-provider' import type { Eip1193Provider } from 'ethers' import { W3mFrameProvider, W3mFrameHelpers, W3mFrameRpcConstants } from '@web3modal/wallet' import type { CombinedProvider } from '@web3modal/scaffold-utils/ethers' +import { BrowserProvider } from 'ethers' +import { JsonRpcSigner } from 'ethers' +import type { JsonRpcApiProvider } from 'ethers' // -- Types --------------------------------------------------------------------- export interface Web3ModalClientOptions extends Omit { @@ -279,6 +286,70 @@ export class Web3Modal extends Web3ModalScaffold { }) return signature as `0x${string}` + }, + + parseUnits: (value: string, decimals: number) => { + return parseUnits(value, decimals) + }, + + formatUnits: (value: bigint, decimals: number) => formatUnits(value, decimals), + + async getEstimatedGas(data) { + const chainId = EthersStoreUtil.state.chainId + const provider = EthersStoreUtil.state.provider + const address = EthersStoreUtil.state.address + + if (!provider) { + throw new Error('connectionControllerClient:sendTransaction - provider is undefined') + } + + if (!address) { + throw new Error('connectionControllerClient:sendTransaction - address is undefined') + } + + const txParams = { + from: data.address, + to: data.to, + value: parseEther(data.value), + data: data.data, + type: 0 + } + + const browserProvider = new BrowserProvider(provider, chainId) + const signer = new JsonRpcSigner(browserProvider, address) + const gas = await signer.estimateGas(txParams) + + return gas + }, + + sendTransaction: async (data: SendTransactionArgs) => { + const chainId = EthersStoreUtil.state.chainId + const provider = EthersStoreUtil.state.provider + const address = EthersStoreUtil.state.address + + if (!provider) { + throw new Error('connectionControllerClient:sendTransaction - provider is undefined') + } + + if (!address) { + throw new Error('connectionControllerClient:sendTransaction - address is undefined') + } + + const txParams = { + to: data.to, + value: data.value, + gasLimit: data.gas, + gasPrice: data.gasPrice, + data: data.data, + type: 0 + } + + const browserProvider = new BrowserProvider(provider, chainId) + const signer = new JsonRpcSigner(browserProvider, address) + const txResponse = await signer.sendTransaction(txParams) + const txReceipt = await txResponse.wait() + + return txReceipt?.hash || null } } diff --git a/packages/ethers5/src/client.ts b/packages/ethers5/src/client.ts index 01510a8ee5..b922f6cd4b 100644 --- a/packages/ethers5/src/client.ts +++ b/packages/ethers5/src/client.ts @@ -8,6 +8,7 @@ import type { LibraryOptions, NetworkControllerClient, PublicStateControllerState, + SendTransactionArgs, Token } from '@web3modal/scaffold' import { Web3ModalScaffold } from '@web3modal/scaffold' @@ -268,12 +269,34 @@ export class Web3Modal extends Web3ModalScaffold { formatUnits: (value: bigint, decimals: number) => ethers.utils.formatUnits(value, decimals), - sendTransaction: async () => { - // Mock await expression, will be implemented - // eslint-disable-next-line no-promise-executor-return - await new Promise(resolve => setTimeout(resolve, 0)) + sendTransaction: async (data: SendTransactionArgs) => { + const provider = EthersStoreUtil.state.provider + const address = EthersStoreUtil.state.address + + if (!provider) { + throw new Error('connectionControllerClient:sendTransaction - provider is undefined') + } + + if (!address) { + throw new Error('connectionControllerClient:sendTransaction - address is undefined') + } + + const txParams = { + to: data.to, + value: data.value, + gasLimit: data.gas, + gasPrice: data.gasPrice, + data: data.data, + type: 0 + } + + const browserProvider = new ethers.providers.Web3Provider(provider) + const signer = browserProvider.getSigner() + + const txResponse = await signer.sendTransaction(txParams) + const txReceipt = await txResponse.wait() - return '0x' + return txReceipt?.blockHash || null } } diff --git a/packages/scaffold/src/views/w3m-convert-preview-view/index.ts b/packages/scaffold/src/views/w3m-convert-preview-view/index.ts index e250c0fd8c..62fd7d1c57 100644 --- a/packages/scaffold/src/views/w3m-convert-preview-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-preview-view/index.ts @@ -5,7 +5,7 @@ import { AccountController, NetworkController, RouterController, - SwapApiController + ConvertController } from '@web3modal/core' import { state } from 'lit/decorators.js' @@ -16,29 +16,29 @@ export class W3mConvertPreviewView extends LitElement { private unsubscribe: ((() => void) | undefined)[] = [] // -- State & Properties -------------------------------- // - @state() private sourceToken = SwapApiController.state.sourceToken + @state() private sourceToken = ConvertController.state.sourceToken - @state() private sourceTokenAmount = SwapApiController.state.sourceTokenAmount ?? '' + @state() private sourceTokenAmount = ConvertController.state.sourceTokenAmount ?? '' - @state() private sourceTokenPriceInUSD = SwapApiController.state.sourceTokenPriceInUSD + @state() private sourceTokenPriceInUSD = ConvertController.state.sourceTokenPriceInUSD - @state() private toToken = SwapApiController.state.toToken + @state() private toToken = ConvertController.state.toToken - @state() private toTokenAmount = SwapApiController.state.toTokenAmount ?? '' + @state() private toTokenAmount = ConvertController.state.toTokenAmount ?? '' - @state() private toTokenPriceInUSD = SwapApiController.state.toTokenPriceInUSD + @state() private toTokenPriceInUSD = ConvertController.state.toTokenPriceInUSD @state() private caipNetwork = NetworkController.state.caipNetwork - @state() private isTransactionPending = SwapApiController.state.isTransactionPending + @state() private transactionLoading = ConvertController.state.transactionLoading @state() private balanceSymbol = AccountController.state.balanceSymbol - @state() private gasPriceInUSD = SwapApiController.state.gasPriceInUSD + @state() private gasPriceInUSD = ConvertController.state.gasPriceInUSD - @state() private priceImpact = SwapApiController.state.priceImpact + @state() private priceImpact = ConvertController.state.priceImpact - @state() private maxSlippage = SwapApiController.state.maxSlippage + @state() private maxSlippage = ConvertController.state.maxSlippage // -- Lifecycle ----------------------------------------- // public constructor() { @@ -57,13 +57,13 @@ export class W3mConvertPreviewView extends LitElement { this.caipNetwork = newCaipNetwork } }), - SwapApiController.subscribe(newState => { + ConvertController.subscribe(newState => { this.sourceToken = newState.sourceToken this.gasPriceInUSD = newState.gasPriceInUSD this.toToken = newState.toToken - this.isTransactionPending = newState.isTransactionPending + this.transactionLoading = newState.transactionLoading this.gasPriceInUSD = newState.gasPriceInUSD - this.isTransactionPending = newState.isTransactionPending + this.transactionLoading = newState.transactionLoading this.toTokenPriceInUSD = newState.toTokenPriceInUSD this.sourceTokenAmount = newState.sourceTokenAmount ?? '' this.toTokenAmount = newState.toTokenAmount ?? '' @@ -164,17 +164,17 @@ export class W3mConvertPreviewView extends LitElement { > @@ -209,7 +209,7 @@ export class W3mConvertPreviewView extends LitElement { } private async onSendTransaction() { - await SwapApiController.swapTokens() + await ConvertController.sendTransaction() } } diff --git a/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts b/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts index 909bf449ef..c17064d06c 100644 --- a/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts @@ -1,8 +1,11 @@ import { customElement, interpolate } from '@web3modal/ui' import { LitElement, html } from 'lit' import styles from './styles.js' -import { ConnectionController, RouterController, SwapApiController } from '@web3modal/core' -import type { TokenInfo } from '@web3modal/core/src/controllers/SwapApiController.js' +import { ConnectionController, RouterController, ConvertController } from '@web3modal/core' +import type { + TokenInfo, + TokenInfoWithBalance +} from '@web3modal/core/src/controllers/ConvertController.js' import { state } from 'lit/decorators.js' @customElement('w3m-convert-select-token-view') @@ -14,9 +17,9 @@ export class W3mConvertSelectTokenView extends LitElement { // -- State & Properties -------------------------------- // @state() private targetToken = RouterController.state.data?.target - @state() private sourceToken = SwapApiController.state.sourceToken + @state() private sourceToken = ConvertController.state.sourceToken - @state() private toToken = SwapApiController.state.toToken + @state() private toToken = ConvertController.state.toToken @state() private searchValue = '' @@ -26,7 +29,7 @@ export class W3mConvertSelectTokenView extends LitElement { this.unsubscribe.push( ...[ - SwapApiController.subscribe(newState => { + ConvertController.subscribe(newState => { this.sourceToken = newState.sourceToken this.toToken = newState.toToken }) @@ -36,11 +39,10 @@ export class W3mConvertSelectTokenView extends LitElement { private onSelectToken(token: TokenInfo) { if (this.targetToken === 'sourceToken') { - SwapApiController.setSourceToken(token) + ConvertController.setSourceToken(token) } else { - SwapApiController.setToToken(token) + ConvertController.setToToken(token) } - SwapApiController.getTokenSwapInfo() RouterController.goBack() } @@ -93,15 +95,17 @@ export class W3mConvertSelectTokenView extends LitElement { } private templateTokens() { - const yourTokens = SwapApiController.state.myTokensWithBalance - ? Object.values(SwapApiController.state.myTokensWithBalance) + const yourTokens = ConvertController.state.myTokensWithBalance + ? Object.values(ConvertController.state.myTokensWithBalance) : [] - const tokens = SwapApiController.state.popularTokens - ? Object.values(SwapApiController.state.popularTokens) + const tokens = ConvertController.state.popularTokens + ? Object.values(ConvertController.state.popularTokens) : [] - const filteredYourTokens = this.filterTokensWithText(yourTokens, this.searchValue) - const filteredTokens = this.filterTokensWithText(tokens, this.searchValue) + const filteredYourTokens: TokenInfoWithBalance[] = this.filterTokensWithText< + TokenInfoWithBalance[] + >(yourTokens, this.searchValue) + const filteredTokens = this.filterTokensWithText(tokens, this.searchValue) return html` @@ -119,14 +123,10 @@ export class W3mConvertSelectTokenView extends LitElement { return html` { if (!selected) { @@ -165,8 +165,8 @@ export class W3mConvertSelectTokenView extends LitElement { } private templateSuggestedTokens() { - const tokens = SwapApiController.state.popularTokens - ? Object.values(SwapApiController.state.popularTokens).slice(0, 8) + const tokens = ConvertController.state.popularTokens + ? Object.values(ConvertController.state.popularTokens).slice(0, 8) : null if (!tokens) { @@ -237,10 +237,10 @@ export class W3mConvertSelectTokenView extends LitElement { ) } - private filterTokensWithText(tokens: TokenInfo[], text: string) { + private filterTokensWithText(tokens: TokenInfo[], text: string) { return tokens.filter(token => `${token.symbol} ${token.name} ${token.address}`.toLowerCase().includes(text.toLowerCase()) - ) + ) as T } } diff --git a/packages/scaffold/src/views/w3m-convert-view/index.ts b/packages/scaffold/src/views/w3m-convert-view/index.ts index 504707a99f..1a6e6ebb8b 100644 --- a/packages/scaffold/src/views/w3m-convert-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-view/index.ts @@ -3,14 +3,13 @@ import { LitElement, html } from 'lit' import { state } from 'lit/decorators.js' import styles from './styles.js' import { - SwapApiController, + ConvertController, RouterController, CoreHelperUtil, NetworkController, - ConnectionController, ModalController } from '@web3modal/core' -import type { TokenInfo } from '@web3modal/core/src/controllers/SwapApiController.js' +import type { TokenInfo } from '@web3modal/core/src/controllers/ConvertController.js' type Target = 'sourceToken' | 'toToken' @@ -23,35 +22,38 @@ export class W3mConvertView extends LitElement { // -- State & Properties -------------------------------- // @state() private caipNetworkId = NetworkController.state.caipNetwork?.id - @state() private initialLoading = SwapApiController.state.initialLoading + @state() private approvalTransaction = ConvertController.state.approvalTransaction - @state() private isTransactionPending = SwapApiController.state.isTransactionPending + @state() private convertTransaction = ConvertController.state.convertTransaction - @state() private loading = SwapApiController.state.loading + @state() private initialized = ConvertController.state.initialized - @state() private loadingPrices = SwapApiController.state.loadingPrices + @state() private loading = ConvertController.state.loading - @state() private sourceToken = SwapApiController.state.sourceToken + @state() private loadingPrices = ConvertController.state.loadingPrices - @state() private sourceTokenAmount = SwapApiController.state.sourceTokenAmount + @state() private sourceToken = ConvertController.state.sourceToken - @state() private sourceTokenPriceInUSD = SwapApiController.state.sourceTokenPriceInUSD + @state() private sourceTokenAmount = ConvertController.state.sourceTokenAmount - @state() private toToken = SwapApiController.state.toToken + @state() private sourceTokenPriceInUSD = ConvertController.state.sourceTokenPriceInUSD - @state() private toTokenAmount = SwapApiController.state.toTokenAmount + @state() private toToken = ConvertController.state.toToken - @state() private toTokenPriceInUSD = SwapApiController.state.toTokenPriceInUSD + @state() private toTokenAmount = ConvertController.state.toTokenAmount - @state() private hasAllowance = SwapApiController.state.hasAllowance + @state() private toTokenPriceInUSD = ConvertController.state.toTokenPriceInUSD - @state() private gasPriceInUSD = SwapApiController.state.gasPriceInUSD + @state() private inputError = ConvertController.state.inputError - @state() private priceImpact = SwapApiController.state.priceImpact + @state() private gasPriceInUSD = ConvertController.state.gasPriceInUSD - @state() private maxSlippage = SwapApiController.state.maxSlippage + @state() private priceImpact = ConvertController.state.priceImpact - @state() private swapErrorMessage = SwapApiController.state.swapErrorMessage + @state() private maxSlippage = ConvertController.state.maxSlippage + + @state() private transactionError = ConvertController.state.transactionError + @state() private transactionLoading = ConvertController.state.transactionLoading // -- Lifecycle ----------------------------------------- // public constructor() { @@ -60,11 +62,11 @@ export class W3mConvertView extends LitElement { NetworkController.subscribeKey('caipNetwork', newCaipNetwork => { if (this.caipNetworkId !== newCaipNetwork?.id) { this.caipNetworkId = newCaipNetwork?.id - SwapApiController.setSourceToken(undefined) - SwapApiController.setToToken(undefined) - SwapApiController.clearMyTokens() - SwapApiController.clearTokens() - SwapApiController.getTokenList({ forceRefetch: true }) + ConvertController.setSourceToken(undefined) + ConvertController.setToToken(undefined) + ConvertController.clearMyTokens() + ConvertController.clearTokens() + ConvertController.getTokenList() } }) @@ -72,34 +74,36 @@ export class W3mConvertView extends LitElement { ...[ ModalController.subscribeKey('open', isOpen => { if (!isOpen) { - SwapApiController.resetState() + ConvertController.resetState() } }), RouterController.subscribeKey('view', newRoute => { if (!newRoute.includes('Convert')) { - SwapApiController.resetState() + ConvertController.resetState() } }), - SwapApiController.subscribeKey('sourceToken', newSourceToken => { + ConvertController.subscribeKey('sourceToken', newSourceToken => { this.sourceToken = newSourceToken }), - SwapApiController.subscribeKey('toToken', newToToken => { + ConvertController.subscribeKey('toToken', newToToken => { this.toToken = newToToken }), - SwapApiController.subscribe(newState => { - this.initialLoading = newState.initialLoading - this.isTransactionPending = newState.isTransactionPending + ConvertController.subscribe(newState => { + this.initialized = newState.initialized this.loading = newState.loading this.loadingPrices = newState.loadingPrices + this.transactionError = newState.transactionError + this.transactionLoading = newState.transactionLoading this.sourceToken = newState.sourceToken this.sourceTokenAmount = newState.sourceTokenAmount this.sourceTokenPriceInUSD = newState.sourceTokenPriceInUSD this.toToken = newState.toToken this.toTokenAmount = newState.toTokenAmount this.toTokenPriceInUSD = newState.toTokenPriceInUSD - this.hasAllowance = newState.hasAllowance + this.approvalTransaction = newState.approvalTransaction + this.convertTransaction = newState.convertTransaction + this.inputError = newState.inputError this.gasPriceInUSD = newState.gasPriceInUSD - this.swapErrorMessage = newState.swapErrorMessage this.priceImpact = newState.priceImpact this.maxSlippage = newState.maxSlippage }) @@ -108,8 +112,8 @@ export class W3mConvertView extends LitElement { } public override firstUpdated() { - if (!this.initialLoading) { - SwapApiController.getTokenList() + if (!this.initialized) { + ConvertController.initializeState() } } @@ -121,9 +125,7 @@ export class W3mConvertView extends LitElement { public override render() { return html` - ${this.initialLoading || this.isTransactionPending - ? this.templateLoading() - : this.templateSwap()} + ${!this.initialized ? this.templateLoading() : this.templateSwap()} ` } @@ -147,34 +149,19 @@ export class W3mConvertView extends LitElement { } private actionButtonLabel(): string { - const myToken = SwapApiController.state.myTokensWithBalance?.[this.sourceToken?.address ?? ''] - const myTokenAmount = myToken - ? parseFloat( - ConnectionController.formatUnits(BigInt(myToken.balance), myToken.decimals) - ).toFixed(3) - : 0 - - if (myTokenAmount === 0) { - return 'Insufficient funds' - } - - if (this.swapErrorMessage) { - if (this.swapErrorMessage?.includes('insufficient funds')) { - return 'Insufficient funds' - } - - return 'Error' + if (this.inputError) { + return this.inputError } - if (!this.toToken || !this.sourceToken) { - return 'Select token' + if (this.approvalTransaction) { + return 'Approve & Convert' } - if (!this.toTokenAmount || !this.sourceTokenAmount) { - return 'Enter amount' + if (this.loading || this.loadingPrices) { + return 'Loading...' } - return this.hasAllowance ? 'Review convert' : 'Not permitted' + return 'Convert' } private templateReplaceTokensButton() { @@ -226,10 +213,14 @@ export class W3mConvertView extends LitElement { } private templateTokenInput(target: Target, token?: TokenInfo) { - const myToken = SwapApiController.state.myTokensWithBalance?.[token?.address ?? ''] + const myToken = ConvertController.state.myTokensWithBalance?.[token?.address ?? ''] const amount = target === 'toToken' ? this.toTokenAmount : this.sourceTokenAmount const price = target === 'toToken' ? this.toTokenPriceInUSD : this.sourceTokenPriceInUSD - const value = parseFloat(amount) * price + let value = parseFloat(amount) * price + + if (target === 'toToken') { + value = value - (this.gasPriceInUSD || 0) + } return html`` } private templateDetails() { + if (this.loading || this.inputError) { + return null + } + if (!this.sourceToken || !this.toToken || !this.sourceTokenAmount || !this.toTokenAmount) { return null } @@ -275,17 +265,18 @@ export class W3mConvertView extends LitElement { private handleChangeAmount(target: Target, value: string) { if (target === 'sourceToken') { - SwapApiController.setSourceTokenAmount(value) + ConvertController.setSourceTokenAmount(value) } else { - SwapApiController.setToTokenAmount(value) + ConvertController.setToTokenAmount(value) } - SwapApiController.clearError() + ConvertController.clearError() this.onDebouncedGetSwapCalldata() } private templateActionButton() { const haveNoTokenSelected = !this.toToken || !this.sourceToken - const loading = this.loadingPrices || this.loading + const empty = !this.toToken || !this.sourceToken + const loading = this.loading || this.loadingPrices || this.transactionLoading return html` ${this.actionButtonLabel()} @@ -304,15 +295,19 @@ export class W3mConvertView extends LitElement { } private onDebouncedGetSwapCalldata = CoreHelperUtil.debounce(async () => { - await SwapApiController.getTokenSwapInfo() + await ConvertController.convertTokens() }, 500) private onSwitchTokens() { - SwapApiController.switchTokens() + ConvertController.switchTokens() } private onConvertPreview() { - RouterController.push('ConvertPreview') + if (this.approvalTransaction) { + ConvertController.sendTransactionForApproval(this.approvalTransaction) + } else { + ConvertController.sendTransactionForConvert(this.convertTransaction) + } } } diff --git a/packages/wagmi/src/client.ts b/packages/wagmi/src/client.ts index 62a234ffb8..1e28fd4b4a 100644 --- a/packages/wagmi/src/client.ts +++ b/packages/wagmi/src/client.ts @@ -7,10 +7,12 @@ import { getEnsAvatar, getEnsName, getAccount, + getGasPrice as wagmiGetGasPrice, switchChain, watchAccount, watchConnectors, - waitForTransactionReceipt + waitForTransactionReceipt, + estimateGas } from '@wagmi/core' import { mainnet } from 'viem/chains' import { prepareTransactionRequest, sendTransaction as wagmiSendTransaction } from '@wagmi/core' @@ -28,7 +30,7 @@ import type { SendTransactionArgs, Token } from '@web3modal/scaffold' -import { formatUnits, parseUnits } from 'viem' +import { formatUnits, parseEther, parseGwei, parseUnits } from 'viem' import type { Hex } from 'viem' import { Web3ModalScaffold } from '@web3modal/scaffold' import type { Web3ModalSIWEClient } from '@web3modal/siwe' @@ -174,28 +176,38 @@ export class Web3Modal extends Web3ModalScaffold { signMessage: async message => signMessage(this.wagmiConfig, { message }), - sendTransaction: async (data: SendTransactionArgs) => { - // eslint-disable-next-line no-useless-catch + getGasPrice: async (chainId: number) => + wagmiGetGasPrice(this.wagmiConfig, { + chainId + }), + + getEstimatedGas: async args => { try { - await prepareTransactionRequest(this.wagmiConfig, { - account: data.address, - to: data.to, - value: data.value, - gas: data.gas, - gasPrice: data.gasPrice, - data: data.data, + return await estimateGas(this.wagmiConfig, { + account: args.address, + to: args.to, + data: args.data, type: 'legacy' }) + } catch (error) { + return 0n + } + }, - const tx = await wagmiSendTransaction(this.wagmiConfig, { + sendTransaction: async (data: SendTransactionArgs) => { + try { + const txParams = { account: data.address, to: data.to, value: data.value, gas: data.gas, gasPrice: data.gasPrice, - type: 'legacy', - data: data.data - }) + data: data.data, + type: 'legacy' as const + } + + await prepareTransactionRequest(this.wagmiConfig, txParams) + const tx = await wagmiSendTransaction(this.wagmiConfig, txParams) await waitForTransactionReceipt(this.wagmiConfig, { hash: tx, timeout: 25000 }) From 5d1ca7d21d93adb9aec426de22bc8b2d8b468944 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Fri, 15 Mar 2024 10:59:49 +0300 Subject: [PATCH 26/96] chore: remove convert from default account view --- .../partials/w3m-account-default-widget/index.ts | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/packages/scaffold/src/partials/w3m-account-default-widget/index.ts b/packages/scaffold/src/partials/w3m-account-default-widget/index.ts index 66257bf94b..ca920b6815 100644 --- a/packages/scaffold/src/partials/w3m-account-default-widget/index.ts +++ b/packages/scaffold/src/partials/w3m-account-default-widget/index.ts @@ -145,15 +145,6 @@ export class W3mAccountDefaultWidget extends LitElement { > Activity - - Convert - Date: Fri, 15 Mar 2024 15:25:40 +0300 Subject: [PATCH 27/96] refactor: improve business logics --- .../core/src/controllers/ConvertController.ts | 44 +++++++++++++------ packages/core/src/utils/ConstantsUtil.ts | 10 +++++ .../w3m-account-default-widget/index.ts | 14 ++++++ .../views/w3m-convert-preview-view/index.ts | 28 ++++++++++-- .../src/views/w3m-convert-view/index.ts | 24 +++------- .../composites/wui-convert-details/index.ts | 6 +-- 6 files changed, 86 insertions(+), 40 deletions(-) diff --git a/packages/core/src/controllers/ConvertController.ts b/packages/core/src/controllers/ConvertController.ts index 08e57a60bf..12888bdb40 100644 --- a/packages/core/src/controllers/ConvertController.ts +++ b/packages/core/src/controllers/ConvertController.ts @@ -7,6 +7,7 @@ import { ConstantsUtil } from '../utils/ConstantsUtil.js' import { ConnectionController } from './ConnectionController.js' import { ConvertApiController, type TransactionData } from './ConvertApiController.js' import { SnackController } from './SnackController.js' +import { RouterController } from './RouterController.js' const CURRENT_CHAIN_ADDRESS = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee' export const DEFAULT_SLIPPAGE_TOLERANCE = '0.5' @@ -192,16 +193,23 @@ export const ConvertController = { }, setToToken(toToken: TokenInfo | undefined) { - if (!toToken) { + if (!toToken || !toToken.address) { return } state.toToken = toToken - if (toToken.address && !state.tokensPriceMap[toToken.address]) { - this.setTokenPriceToMap(toToken.address) + if (!state.tokensPriceMap[toToken.address]) { + this.checkToTokenValues(toToken.address) + } else { + state.toTokenPriceInUSD = parseFloat(state.tokensPriceMap[toToken.address] || '0') } }, + async checkToTokenValues(address: string) { + const price = await this.setTokenPriceToMap(address) + state.toTokenPriceInUSD = parseFloat(price) + }, + setLoading(isLoading: boolean) { state.loading = isLoading }, @@ -264,16 +272,6 @@ export const ConvertController = { } const priceChange = sourceTokenValue - toTokenValue - console.log( - '>>> calculatePriceDifference', - priceChange, - toTokenValue, - sourceTokenValue, - state.sourceTokenAmount, - state.sourceTokenPriceInUSD, - state.toTokenAmount, - state.toTokenPriceInUSD - ) const changeInPercentage = (priceChange / toTokenValue) * 100 let direction: PriceDifferenceDirection = 'none' @@ -330,9 +328,11 @@ export const ConvertController = { const prices = await ConvertApiController.getTokenPriceWithAddresses([address]) const price = prices[address] || '0' state.tokensPriceMap[address] = price + if (address === CURRENT_CHAIN_ADDRESS) { state.networkPrice = price } + return price }, async getNetworkTokenPrice() { @@ -446,6 +446,17 @@ export const ConvertController = { if (!hasAllowance) { const transaction = await this.createTokenAllowance() state.approvalTransaction = transaction + const toTokenConvertedAmount = + state.sourceTokenPriceInUSD && state.toTokenPriceInUSD + ? (1 / state.toTokenPriceInUSD) * state.sourceTokenPriceInUSD + : 0 + let floatNumber = toTokenConvertedAmount + let scaleFactor = 10000000000 // 10^10 to keep 10 decimal places + let scaledNumber = floatNumber * scaleFactor + let bigIntString = Math.round(scaledNumber).toString() + + this.setToTokenAmountValue(bigIntString, toTokenDecimals, toTokenAddress) + this.setTransactionDetails(transaction.gas, transaction.gasPrice) } else { state.approvalTransaction = undefined const response = await this.createConvert() @@ -496,7 +507,7 @@ export const ConvertController = { }) state.approvalTransaction = undefined - state.transactionLoading = true + state.transactionLoading = false } catch (error) { state.transactionError = error?.shortMessage state.transactionLoading = false @@ -542,6 +553,11 @@ export const ConvertController = { value: BigInt(data.value), chainId: CoreHelperUtil.getEvmChainId(NetworkController.state.caipNetwork?.id) }) + state.transactionLoading = false + RouterController.replace('Transactions') + setTimeout(() => { + this.resetState() + }, 1000) return transactionHash } catch (error) { state.transactionError = error?.shortMessage diff --git a/packages/core/src/utils/ConstantsUtil.ts b/packages/core/src/utils/ConstantsUtil.ts index 390b43fe3d..2a29f3c0fc 100644 --- a/packages/core/src/utils/ConstantsUtil.ts +++ b/packages/core/src/utils/ConstantsUtil.ts @@ -71,6 +71,16 @@ export const ConstantsUtil = { 'UNI', '1INCH', 'AAVE', + 'SOL', + 'ADA', + 'AVAX', + 'DOT', + 'LINK', + 'NITRO', + 'GAIA', + 'MILK', + 'TRX', + 'NEAR', 'GNO', 'WBTC', 'DAI', diff --git a/packages/scaffold/src/partials/w3m-account-default-widget/index.ts b/packages/scaffold/src/partials/w3m-account-default-widget/index.ts index ca920b6815..66257bf94b 100644 --- a/packages/scaffold/src/partials/w3m-account-default-widget/index.ts +++ b/packages/scaffold/src/partials/w3m-account-default-widget/index.ts @@ -145,6 +145,15 @@ export class W3mAccountDefaultWidget extends LitElement { > Activity + + Convert + void) | undefined)[] = [] // -- State & Properties -------------------------------- // + @state() private detailsOpen = true + + @state() private approvalTransaction = ConvertController.state.approvalTransaction + + @state() private convertTransaction = ConvertController.state.convertTransaction + @state() private sourceToken = ConvertController.state.sourceToken @state() private sourceTokenAmount = ConvertController.state.sourceTokenAmount ?? '' @@ -58,6 +64,8 @@ export class W3mConvertPreviewView extends LitElement { } }), ConvertController.subscribe(newState => { + this.approvalTransaction = newState.approvalTransaction + this.convertTransaction = newState.convertTransaction this.sourceToken = newState.sourceToken this.gasPriceInUSD = newState.gasPriceInUSD this.toToken = newState.toToken @@ -176,7 +184,9 @@ export class W3mConvertPreviewView extends LitElement { > ${this.transactionLoading ? html`` - : html`Convert`} + : html`${this.actionButtonLabel()}`} @@ -191,7 +201,7 @@ export class W3mConvertPreviewView extends LitElement { return html` void) | undefined)[] = [] // -- State & Properties -------------------------------- // - @state() private caipNetworkId = NetworkController.state.caipNetwork?.id - - @state() private approvalTransaction = ConvertController.state.approvalTransaction + @state() private detailsOpen = false - @state() private convertTransaction = ConvertController.state.convertTransaction + @state() private caipNetworkId = NetworkController.state.caipNetwork?.id @state() private initialized = ConvertController.state.initialized @@ -153,15 +151,7 @@ export class W3mConvertView extends LitElement { return this.inputError } - if (this.approvalTransaction) { - return 'Approve & Convert' - } - - if (this.loading || this.loadingPrices) { - return 'Loading...' - } - - return 'Convert' + return 'Review convert' } private templateReplaceTokensButton() { @@ -250,7 +240,7 @@ export class W3mConvertView extends LitElement { return html` Date: Fri, 15 Mar 2024 16:25:18 +0300 Subject: [PATCH 28/96] refactor: linter and build issues --- packages/core/index.ts | 1 - .../src/controllers/ConnectionController.ts | 18 +- .../src/controllers/ConvertApiController.ts | 32 +--- .../core/src/controllers/ConvertController.ts | 157 +++++++++++------- packages/core/src/utils/TypeUtil.ts | 7 +- .../controllers/ConnectionController.test.ts | 2 + packages/ethers/src/client.ts | 13 +- packages/ethers5/src/client.ts | 2 +- .../views/w3m-convert-preview-view/index.ts | 2 +- .../w3m-convert-select-token-view/index.ts | 2 +- .../src/views/w3m-convert-view/index.ts | 9 +- packages/wagmi/src/client.ts | 13 +- 12 files changed, 131 insertions(+), 127 deletions(-) diff --git a/packages/core/index.ts b/packages/core/index.ts index 1f63db62c1..6621f60826 100644 --- a/packages/core/index.ts +++ b/packages/core/index.ts @@ -56,7 +56,6 @@ export { TransactionsController } from './src/controllers/TransactionsController export type { TransactionsControllerState } from './src/controllers/TransactionsController.js' export { ConvertApiController } from './src/controllers/ConvertApiController.js' -export type { ConvertApiControllerState } from './src/controllers/ConvertApiController.js' export { ConvertController } from './src/controllers/ConvertController.js' export type { ConvertControllerState } from './src/controllers/ConvertController.js' diff --git a/packages/core/src/controllers/ConnectionController.ts b/packages/core/src/controllers/ConnectionController.ts index db3336d6ba..afe889d705 100644 --- a/packages/core/src/controllers/ConnectionController.ts +++ b/packages/core/src/controllers/ConnectionController.ts @@ -2,7 +2,12 @@ import { subscribeKey as subKey } from 'valtio/utils' import { proxy, ref } from 'valtio/vanilla' import { CoreHelperUtil } from '../utils/CoreHelperUtil.js' import { StorageUtil } from '../utils/StorageUtil.js' -import type { Connector, SendTransactionArgs, WcWallet } from '../utils/TypeUtil.js' +import type { + Connector, + EstimateGasTransactionArgs, + SendTransactionArgs, + WcWallet +} from '../utils/TypeUtil.js' import { TransactionsController } from './TransactionsController.js' // -- Types --------------------------------------------- // @@ -17,9 +22,8 @@ export interface ConnectionControllerClient { connectWalletConnect: (onUri: (uri: string) => void) => Promise disconnect: () => Promise signMessage: (message: string) => Promise - sendTransaction: (args: SendTransactionArgs) => Promise - getGasPrice: (chainId: number) => Promise - getEstimatedGas: (args: any) => Promise + sendTransaction: (args: SendTransactionArgs) => Promise<`0x${string}` | null> + getEstimatedGas: (args: EstimateGasTransactionArgs) => Promise parseUnits: (value: string, decimals: number) => bigint formatUnits: (value: bigint, decimals: number) => string connectExternal?: (options: ConnectExternalOptions) => Promise @@ -100,11 +104,7 @@ export const ConnectionController = { return this._getClient().sendTransaction(args) }, - async getGasPrice(chainId: number) { - return this._getClient().getGasPrice(chainId) - }, - - async getEstimatedGas(args: any) { + async getEstimatedGas(args: EstimateGasTransactionArgs) { return this._getClient().getEstimatedGas(args) }, diff --git a/packages/core/src/controllers/ConvertApiController.ts b/packages/core/src/controllers/ConvertApiController.ts index a280f26b47..ad26fd16bc 100644 --- a/packages/core/src/controllers/ConvertApiController.ts +++ b/packages/core/src/controllers/ConvertApiController.ts @@ -1,5 +1,3 @@ -import { subscribeKey as subKey } from 'valtio/utils' -import { proxy, subscribe as sub } from 'valtio/vanilla' import { CoreHelperUtil } from '../utils/CoreHelperUtil.js' import { NetworkController } from './NetworkController.js' import { FetchUtil } from '../utils/FetchUtil.js' @@ -26,9 +24,6 @@ const OneInchAPIEndpoints = { } // -- Types --------------------------------------------- // -// #region Types -export interface ConvertApiControllerState {} - export type TokenInfo = { address: `0x${string}` symbol: string @@ -95,27 +90,8 @@ export type GetConvertDataResponse = { tx: TransactionData } -type StateKey = keyof ConvertApiControllerState -// #endregion - -// -- State --------------------------------------------- // -const state = proxy({}) - // -- Controller ---------------------------------------- // export const ConvertApiController = { - state, - - subscribe(callback: (newState: ConvertApiControllerState) => void) { - return sub(state, () => callback(state)) - }, - - subscribeKey( - key: K, - callback: (value: ConvertApiControllerState[K]) => void - ) { - return subKey(state, key, callback) - }, - get1InchAPI() { const api = new FetchUtil({ baseUrl: ONEINCH_API_BASE_URL }) const chainId = CoreHelperUtil.getEvmChainId(NetworkController.state.caipNetwork?.id) @@ -278,8 +254,8 @@ export const ConvertApiController = { tokensPrice: Record ) { const mergedTokens = Object.entries(tokens).reduce>( - (mergedTokens, [tokenAddress, tokenInfo]) => { - mergedTokens[tokenAddress] = { + (_mergedTokens, [tokenAddress, tokenInfo]) => { + _mergedTokens[tokenAddress] = { ...tokenInfo, balance: ConnectionController.formatUnits( BigInt(balances[tokenAddress] ?? '0'), @@ -288,7 +264,7 @@ export const ConvertApiController = { price: tokensPrice[tokenAddress] ?? '0' } - return mergedTokens + return _mergedTokens }, {} ) @@ -306,7 +282,7 @@ export const ConvertApiController = { }: GetConvertDataParams): Promise { const { api, paths } = this.get1InchAPI() - return await api.get({ + return await api.get({ path: paths.swap, params: { src: sourceTokenAddress, diff --git a/packages/core/src/controllers/ConvertController.ts b/packages/core/src/controllers/ConvertController.ts index 12888bdb40..4d463fa920 100644 --- a/packages/core/src/controllers/ConvertController.ts +++ b/packages/core/src/controllers/ConvertController.ts @@ -1,11 +1,9 @@ import { subscribeKey as subKey } from 'valtio/utils' import { proxy, subscribe as sub } from 'valtio/vanilla' -import { CoreHelperUtil } from '../utils/CoreHelperUtil.js' -import { NetworkController } from './NetworkController.js' import { AccountController } from './AccountController.js' import { ConstantsUtil } from '../utils/ConstantsUtil.js' import { ConnectionController } from './ConnectionController.js' -import { ConvertApiController, type TransactionData } from './ConvertApiController.js' +import { ConvertApiController } from './ConvertApiController.js' import { SnackController } from './SnackController.js' import { RouterController } from './RouterController.js' @@ -15,6 +13,24 @@ export const DEFAULT_SLIPPAGE_TOLERANCE = '0.5' // -- Types --------------------------------------------- // type PriceDifferenceDirection = 'up' | 'down' | 'none' +type TransactionParams = { + data: `0x${string}` + to: `0x${string}` + gas: bigint + gasPrice: bigint + value: bigint +} + +class TransactionError extends Error { + shortMessage?: string + + constructor(message?: string, shortMessage?: string) { + super(message) + this.name = 'TransactionError' + this.shortMessage = shortMessage + } +} + export interface ConvertControllerState { // Loading states initialized: boolean @@ -22,9 +38,9 @@ export interface ConvertControllerState { loading?: boolean // Approval & Convert transaction states - approvalTransaction: object | undefined + approvalTransaction: TransactionParams | undefined convertLoading: boolean - convertTransaction?: TransactionData + convertTransaction: TransactionParams | undefined transactionLoading?: boolean transactionError?: string @@ -193,15 +209,15 @@ export const ConvertController = { }, setToToken(toToken: TokenInfo | undefined) { - if (!toToken || !toToken.address) { + if (!toToken?.address) { return } state.toToken = toToken - if (!state.tokensPriceMap[toToken.address]) { - this.checkToTokenValues(toToken.address) - } else { + if (state.tokensPriceMap[toToken.address]) { state.toTokenPriceInUSD = parseFloat(state.tokensPriceMap[toToken.address] || '0') + } else { + this.checkToTokenValues(toToken.address) } }, @@ -215,8 +231,8 @@ export const ConvertController = { }, switchTokens() { - const newSourceToken = state.toToken ? Object.assign({}, state.toToken) : undefined - const newToToken = state.sourceToken ? Object.assign({}, state.sourceToken) : undefined + const newSourceToken = state.toToken ? { ...state.toToken } : undefined + const newToToken = state.sourceToken ? { ...state.sourceToken } : undefined this.setSourceToken(newSourceToken) this.setToToken(newToToken) @@ -321,6 +337,7 @@ export const ConvertController = { const prices = await ConvertApiController.getTokenPriceWithAddresses([address]) const price = prices[address] || '0' state.tokensPriceMap[address] = price + return parseFloat(price) }, @@ -332,6 +349,7 @@ export const ConvertController = { if (address === CURRENT_CHAIN_ADDRESS) { state.networkPrice = price } + return price }, @@ -350,6 +368,7 @@ export const ConvertController = { state.tokensPriceMap = Object.entries(res).reduce>( (prices, [tokenAddress, tokenInfo]) => { prices[tokenAddress] = tokenInfo.price + return prices }, {} @@ -357,19 +376,15 @@ export const ConvertController = { state.myTokensWithBalance = res }, - async searchTokens(searchTerm: string) { - const response = await ConvertApiController.searchTokens(searchTerm) - }, + calculateGasPriceInEther(gas: bigint, gasPrice: bigint) { + const totalGasCostInWei = gasPrice * gas - calculateGasPriceInEther(gas: number, gasPrice: string) { - const gasPriceNumber = BigInt(gasPrice) - const totalGasCostInWei = gasPriceNumber * BigInt(gas) const totalGasCostInEther = Number(totalGasCostInWei) / 1e18 return totalGasCostInEther }, - calculateGasPriceInUSD(gas: number, gasPrice: string) { + calculateGasPriceInUSD(gas: bigint, gasPrice: bigint) { try { const totalGasCostInEther = this.calculateGasPriceInEther(gas, gasPrice) const networkPriceNumber = parseFloat(state.networkPrice) @@ -443,35 +458,39 @@ export const ConvertController = { sourceTokenDecimals }) - if (!hasAllowance) { - const transaction = await this.createTokenAllowance() - state.approvalTransaction = transaction - const toTokenConvertedAmount = - state.sourceTokenPriceInUSD && state.toTokenPriceInUSD - ? (1 / state.toTokenPriceInUSD) * state.sourceTokenPriceInUSD - : 0 - let floatNumber = toTokenConvertedAmount - let scaleFactor = 10000000000 // 10^10 to keep 10 decimal places - let scaledNumber = floatNumber * scaleFactor - let bigIntString = Math.round(scaledNumber).toString() - - this.setToTokenAmountValue(bigIntString, toTokenDecimals, toTokenAddress) + const toAmount = this.getToAmount() + + if (hasAllowance) { + state.approvalTransaction = undefined + const transaction = await this.createConvert() + state.convertTransaction = transaction + + this.setToTokenAmountValue(toAmount, toTokenDecimals, toTokenAddress) this.setTransactionDetails(transaction.gas, transaction.gasPrice) } else { - state.approvalTransaction = undefined - const response = await this.createConvert() + const transaction = await this.createTokenAllowance() + state.approvalTransaction = transaction - if (response.tx) { - state.convertTransaction = response.tx - this.setToTokenAmountValue(response.toAmount, toTokenDecimals, toTokenAddress) - this.setTransactionDetails(response.tx.gas, response.tx.gasPrice) - } + this.setToTokenAmountValue(toAmount, toTokenDecimals, toTokenAddress) + this.setTransactionDetails(transaction.gas, transaction.gasPrice) } state.loading = false }, - // 3.1.1 Create token allowance + getToAmount() { + const toTokenConvertedAmount = + state.sourceTokenPriceInUSD && state.toTokenPriceInUSD + ? (1 / state.toTokenPriceInUSD) * state.sourceTokenPriceInUSD + : 0 + const floatNumber = toTokenConvertedAmount + const scaleFactor = 10000000000 + const scaledNumber = floatNumber * scaleFactor + const bigIntString = Math.round(scaledNumber).toString() + + return bigIntString + }, + async createTokenAllowance() { const { fromAddress, sourceTokenAddress } = this.getParams() @@ -485,37 +504,42 @@ export const ConvertController = { }) const gasLimit = await ConnectionController.getEstimatedGas({ - ...transaction, - from: fromAddress + address: fromAddress, + to: transaction.to, + data: transaction.data }) return { ...transaction, - gas: gasLimit + gas: gasLimit, + gasPrice: BigInt(transaction.gasPrice), + value: BigInt(transaction.value) } }, - // 3.1.2 Request approval for token - async sendTransactionForApproval(data) { + async sendTransactionForApproval(data: TransactionParams) { const { fromAddress } = this.getParams() state.transactionLoading = true try { await ConnectionController.sendTransaction({ address: fromAddress, - ...data + to: data.to, + data: data.data, + value: BigInt(data.value), + gasPrice: BigInt(data.gasPrice) }) state.approvalTransaction = undefined state.transactionLoading = false - } catch (error) { - state.transactionError = error?.shortMessage + } catch (err) { + const error = err as TransactionError + state.transactionError = error?.shortMessage as unknown as string state.transactionLoading = false SnackController.showError(error?.shortMessage || 'Transaction errror') } }, - // 3.2.1 Create convert async createConvert() { const { fromAddress, @@ -529,17 +553,30 @@ export const ConvertController = { throw new Error('>>> createConvert: Invalid values to start converting') } - return await ConvertApiController.getConvertData({ + const response = await ConvertApiController.getConvertData({ fromAddress, sourceTokenAddress, sourceTokenAmount, toTokenAddress, decimals: sourceTokenDecimals }) + + const transaction = { + data: response.tx.data, + to: response.tx.to, + gas: BigInt(response.tx.gas), + gasPrice: BigInt(response.tx.gasPrice), + value: BigInt(response.tx.value) + } + + return transaction }, - // 3.2.2 Send convert transaction - async sendTransactionForConvert(data) { + async sendTransactionForConvert(data: TransactionParams | undefined) { + if (!data) { + return undefined + } + const { fromAddress } = this.getParams() state.transactionLoading = true @@ -550,28 +587,26 @@ export const ConvertController = { data: data.data, gas: data.gas, gasPrice: BigInt(data.gasPrice), - value: BigInt(data.value), - chainId: CoreHelperUtil.getEvmChainId(NetworkController.state.caipNetwork?.id) + value: BigInt(data.value) }) state.transactionLoading = false RouterController.replace('Transactions') setTimeout(() => { this.resetState() }, 1000) + return transactionHash - } catch (error) { + } catch (err) { + const error = err as TransactionError state.transactionError = error?.shortMessage state.transactionLoading = false SnackController.showError(error?.shortMessage || 'Transaction errror') + return undefined } }, - async setToTokenAmountValue( - toTokenAmount: string, - toTokenDecimals: number, - toTokenAddress: string - ) { + setToTokenAmountValue(toTokenAmount: string, toTokenDecimals: number, toTokenAddress: string) { if (!toTokenAddress || !toTokenAmount) { return } @@ -587,7 +622,7 @@ export const ConvertController = { state.toTokenAmount = toTokenAmountValue.toString() }, - async setTransactionDetails(gas: bigint, gasPrice: string) { + setTransactionDetails(gas: bigint, gasPrice: bigint) { const { toTokenAddress, toTokenAmount } = this.getParams() if (!toTokenAddress) { @@ -597,7 +632,7 @@ export const ConvertController = { const toTokenPrice = state.tokensPriceMap[toTokenAddress] || '0' state.toTokenPriceInUSD = parseFloat(toTokenPrice) - state.gasPriceInUSD = this.calculateGasPriceInUSD(Number(gas), gasPrice) + state.gasPriceInUSD = this.calculateGasPriceInUSD(gas, gasPrice) state.priceImpact = this.calculatePriceImpact(toTokenAmount) state.maxSlippage = this.calculateMaxSlippage() } diff --git a/packages/core/src/utils/TypeUtil.ts b/packages/core/src/utils/TypeUtil.ts index 172ece45e6..97ad775eec 100644 --- a/packages/core/src/utils/TypeUtil.ts +++ b/packages/core/src/utils/TypeUtil.ts @@ -405,6 +405,11 @@ export interface SendTransactionArgs { value: bigint gas?: bigint gasPrice: bigint - chainId: number address: `0x${string}` } + +export interface EstimateGasTransactionArgs { + address: `0x${string}` + to: `0x${string}` + data: `0x${string}` +} diff --git a/packages/core/tests/controllers/ConnectionController.test.ts b/packages/core/tests/controllers/ConnectionController.test.ts index a90ebc0c2a..eb9646b567 100644 --- a/packages/core/tests/controllers/ConnectionController.test.ts +++ b/packages/core/tests/controllers/ConnectionController.test.ts @@ -14,6 +14,7 @@ const client: ConnectionControllerClient = { }, disconnect: async () => Promise.resolve(), signMessage: async (message: string) => Promise.resolve(message), + getEstimatedGas: async () => Promise.resolve(BigInt(0)), connectExternal: async _id => Promise.resolve(), checkInstalled: _id => true, parseUnits: value => BigInt(value), @@ -24,6 +25,7 @@ const client: ConnectionControllerClient = { const partialClient: ConnectionControllerClient = { connectWalletConnect: async () => Promise.resolve(), disconnect: async () => Promise.resolve(), + getEstimatedGas: async () => Promise.resolve(BigInt(0)), signMessage: async (message: string) => Promise.resolve(message), parseUnits: value => BigInt(value), formatUnits: value => value.toString(), diff --git a/packages/ethers/src/client.ts b/packages/ethers/src/client.ts index bc5cac4f41..8f5790e5c0 100644 --- a/packages/ethers/src/client.ts +++ b/packages/ethers/src/client.ts @@ -11,7 +11,7 @@ import type { SendTransactionArgs, Token } from '@web3modal/scaffold' -import { AccountController, Web3ModalScaffold } from '@web3modal/scaffold' +import { Web3ModalScaffold } from '@web3modal/scaffold' import { ConstantsUtil, PresetsUtil, HelpersUtil } from '@web3modal/scaffold-utils' import EthereumProvider from '@walletconnect/ethereum-provider' import type { Web3ModalSIWEClient } from '@web3modal/siwe' @@ -29,8 +29,7 @@ import { InfuraProvider, getAddress as getOriginalAddress, parseUnits, - formatUnits, - parseEther + formatUnits } from 'ethers' import { EthersConstantsUtil, @@ -43,7 +42,6 @@ import { W3mFrameProvider, W3mFrameHelpers, W3mFrameRpcConstants } from '@web3mo import type { CombinedProvider } from '@web3modal/scaffold-utils/ethers' import { BrowserProvider } from 'ethers' import { JsonRpcSigner } from 'ethers' -import type { JsonRpcApiProvider } from 'ethers' // -- Types --------------------------------------------------------------------- export interface Web3ModalClientOptions extends Omit { @@ -288,9 +286,7 @@ export class Web3Modal extends Web3ModalScaffold { return signature as `0x${string}` }, - parseUnits: (value: string, decimals: number) => { - return parseUnits(value, decimals) - }, + parseUnits: (value: string, decimals: number) => parseUnits(value, decimals), formatUnits: (value: bigint, decimals: number) => formatUnits(value, decimals), @@ -310,7 +306,6 @@ export class Web3Modal extends Web3ModalScaffold { const txParams = { from: data.address, to: data.to, - value: parseEther(data.value), data: data.data, type: 0 } @@ -349,7 +344,7 @@ export class Web3Modal extends Web3ModalScaffold { const txResponse = await signer.sendTransaction(txParams) const txReceipt = await txResponse.wait() - return txReceipt?.hash || null + return (txReceipt?.hash as `0x${string}`) || null } } diff --git a/packages/ethers5/src/client.ts b/packages/ethers5/src/client.ts index b922f6cd4b..06df50d18a 100644 --- a/packages/ethers5/src/client.ts +++ b/packages/ethers5/src/client.ts @@ -296,7 +296,7 @@ export class Web3Modal extends Web3ModalScaffold { const txResponse = await signer.sendTransaction(txParams) const txReceipt = await txResponse.wait() - return txReceipt?.blockHash || null + return (txReceipt?.blockHash as `0x${string}`) || null } } diff --git a/packages/scaffold/src/views/w3m-convert-preview-view/index.ts b/packages/scaffold/src/views/w3m-convert-preview-view/index.ts index 6d21f0aa92..9144945dcb 100644 --- a/packages/scaffold/src/views/w3m-convert-preview-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-preview-view/index.ts @@ -226,7 +226,7 @@ export class W3mConvertPreviewView extends LitElement { RouterController.goBack() } - private async onSendTransaction() { + private onSendTransaction() { if (this.approvalTransaction) { ConvertController.sendTransactionForApproval(this.approvalTransaction) } else { diff --git a/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts b/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts index c17064d06c..b1176d7439 100644 --- a/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts @@ -1,7 +1,7 @@ import { customElement, interpolate } from '@web3modal/ui' import { LitElement, html } from 'lit' import styles from './styles.js' -import { ConnectionController, RouterController, ConvertController } from '@web3modal/core' +import { RouterController, ConvertController } from '@web3modal/core' import type { TokenInfo, TokenInfoWithBalance diff --git a/packages/scaffold/src/views/w3m-convert-view/index.ts b/packages/scaffold/src/views/w3m-convert-view/index.ts index c237026be2..b54e981510 100644 --- a/packages/scaffold/src/views/w3m-convert-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-view/index.ts @@ -50,7 +50,6 @@ export class W3mConvertView extends LitElement { @state() private maxSlippage = ConvertController.state.maxSlippage - @state() private transactionError = ConvertController.state.transactionError @state() private transactionLoading = ConvertController.state.transactionLoading // -- Lifecycle ----------------------------------------- // @@ -90,7 +89,6 @@ export class W3mConvertView extends LitElement { this.initialized = newState.initialized this.loading = newState.loading this.loadingPrices = newState.loadingPrices - this.transactionError = newState.transactionError this.transactionLoading = newState.transactionLoading this.sourceToken = newState.sourceToken this.sourceTokenAmount = newState.sourceTokenAmount @@ -98,8 +96,6 @@ export class W3mConvertView extends LitElement { this.toToken = newState.toToken this.toTokenAmount = newState.toTokenAmount this.toTokenPriceInUSD = newState.toTokenPriceInUSD - this.approvalTransaction = newState.approvalTransaction - this.convertTransaction = newState.convertTransaction this.inputError = newState.inputError this.gasPriceInUSD = newState.gasPriceInUSD this.priceImpact = newState.priceImpact @@ -123,7 +119,7 @@ export class W3mConvertView extends LitElement { public override render() { return html` - ${!this.initialized ? this.templateLoading() : this.templateSwap()} + ${this.initialized ? this.templateSwap() : this.templateLoading()} ` } @@ -209,7 +205,7 @@ export class W3mConvertView extends LitElement { let value = parseFloat(amount) * price if (target === 'toToken') { - value = value - (this.gasPriceInUSD || 0) + value -= this.gasPriceInUSD || 0 } return html` diff --git a/packages/wagmi/src/client.ts b/packages/wagmi/src/client.ts index 1e28fd4b4a..43e8b0e252 100644 --- a/packages/wagmi/src/client.ts +++ b/packages/wagmi/src/client.ts @@ -7,7 +7,6 @@ import { getEnsAvatar, getEnsName, getAccount, - getGasPrice as wagmiGetGasPrice, switchChain, watchAccount, watchConnectors, @@ -30,7 +29,7 @@ import type { SendTransactionArgs, Token } from '@web3modal/scaffold' -import { formatUnits, parseEther, parseGwei, parseUnits } from 'viem' +import { formatUnits, parseUnits } from 'viem' import type { Hex } from 'viem' import { Web3ModalScaffold } from '@web3modal/scaffold' import type { Web3ModalSIWEClient } from '@web3modal/siwe' @@ -176,11 +175,6 @@ export class Web3Modal extends Web3ModalScaffold { signMessage: async message => signMessage(this.wagmiConfig, { message }), - getGasPrice: async (chainId: number) => - wagmiGetGasPrice(this.wagmiConfig, { - chainId - }), - getEstimatedGas: async args => { try { return await estimateGas(this.wagmiConfig, { @@ -195,6 +189,8 @@ export class Web3Modal extends Web3ModalScaffold { }, sendTransaction: async (data: SendTransactionArgs) => { + const { chainId } = getAccount(this.wagmiConfig) + try { const txParams = { account: data.address, @@ -203,6 +199,7 @@ export class Web3Modal extends Web3ModalScaffold { gas: data.gas, gasPrice: data.gasPrice, data: data.data, + chainId, type: 'legacy' as const } @@ -213,7 +210,7 @@ export class Web3Modal extends Web3ModalScaffold { return tx } catch (error) { - throw error + return `0x` } }, From a4f6d9584b378a1a1a6e0c18ad46374ae96cd04f Mon Sep 17 00:00:00 2001 From: enesozturk Date: Tue, 19 Mar 2024 19:05:07 +0300 Subject: [PATCH 29/96] refactor: update token button usage --- packages/scaffold/src/views/w3m-convert-preview-view/index.ts | 4 ++-- .../scaffold/src/views/w3m-convert-select-token-view/index.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/scaffold/src/views/w3m-convert-preview-view/index.ts b/packages/scaffold/src/views/w3m-convert-preview-view/index.ts index 9144945dcb..2902acb6e3 100644 --- a/packages/scaffold/src/views/w3m-convert-preview-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-preview-view/index.ts @@ -117,7 +117,7 @@ export class W3mConvertPreviewView extends LitElement { @@ -150,7 +150,7 @@ export class W3mConvertPreviewView extends LitElement { diff --git a/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts b/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts index b1176d7439..cdeaec95db 100644 --- a/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts @@ -179,7 +179,7 @@ export class W3mConvertSelectTokenView extends LitElement { tokenInfo => html` this.onSelectToken(tokenInfo)} > From 4e2b47c17b491b994f39dca1a7d5fa66c4ddcc01 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Thu, 21 Mar 2024 13:32:31 +0300 Subject: [PATCH 30/96] feat: add gas price checking --- .../src/controllers/ConvertApiController.ts | 22 ++++++++++++- .../core/src/controllers/ConvertController.ts | 30 +++++++++++++---- .../views/w3m-convert-preview-view/index.ts | 3 +- .../src/views/w3m-convert-view/index.ts | 33 ++++++++++++++++++- .../composites/wui-convert-details/index.ts | 2 +- .../src/composites/wui-convert-input/index.ts | 18 ++++++---- packages/wagmi/src/client.ts | 32 ++++++++---------- 7 files changed, 105 insertions(+), 35 deletions(-) diff --git a/packages/core/src/controllers/ConvertApiController.ts b/packages/core/src/controllers/ConvertApiController.ts index ad26fd16bc..274bb89ae3 100644 --- a/packages/core/src/controllers/ConvertApiController.ts +++ b/packages/core/src/controllers/ConvertApiController.ts @@ -90,6 +90,26 @@ export type GetConvertDataResponse = { tx: TransactionData } +export type GetGasPricesResponse = { + baseFree: string + low: { + maxPriorityFeePerGas: string + maxFeePerGas: string + } + medium: { + maxPriorityFeePerGas: string + maxFeePerGas: string + } + high: { + maxPriorityFeePerGas: string + maxFeePerGas: string + } + instant: { + maxPriorityFeePerGas: string + maxFeePerGas: string + } +} + // -- Controller ---------------------------------------- // export const ConvertApiController = { get1InchAPI() { @@ -131,7 +151,7 @@ export const ConvertApiController = { async getGasPrice() { const { api, paths } = this.get1InchAPI() - const gasPrices = await api.get>({ + const gasPrices = await api.get({ path: paths.gasPrice, headers: { 'content-type': 'application/json' } }) diff --git a/packages/core/src/controllers/ConvertController.ts b/packages/core/src/controllers/ConvertController.ts index 4d463fa920..15bb612d05 100644 --- a/packages/core/src/controllers/ConvertController.ts +++ b/packages/core/src/controllers/ConvertController.ts @@ -66,6 +66,7 @@ export interface ConvertControllerState { tokensPriceMap: Record // Calculations + gasFee: bigint gasPriceInUSD?: number priceImpact: number | undefined maxSlippage: number | undefined @@ -130,6 +131,7 @@ const state = proxy({ tokensPriceMap: {}, // Calculations + gasFee: BigInt(0), gasPriceInUSD: 0, priceImpact: undefined, maxSlippage: undefined @@ -376,9 +378,13 @@ export const ConvertController = { state.myTokensWithBalance = res }, + async setGasFee() { + const res = await ConvertApiController.getGasPrice() + state.gasFee = BigInt(res.instant.maxFeePerGas) + }, + calculateGasPriceInEther(gas: bigint, gasPrice: bigint) { const totalGasCostInWei = gasPrice * gas - const totalGasCostInEther = Number(totalGasCostInWei) / 1e18 return totalGasCostInEther @@ -396,16 +402,19 @@ export const ConvertController = { } }, - calculatePriceImpact(_toTokenAmount: string) { + calculatePriceImpact(_toTokenAmount: string, _gasPriceInUSD: number = 0) { const sourceTokenAmount = parseFloat(state.sourceTokenAmount) const toTokenAmount = parseFloat(_toTokenAmount) const sourceTokenPrice = state.sourceTokenPriceInUSD const toTokenPrice = state.toTokenPriceInUSD - const effectivePrice = (sourceTokenAmount * sourceTokenPrice) / toTokenAmount - const priceImpact = ((effectivePrice - toTokenPrice) / toTokenPrice) * 100 + const totalSourceCostUSD = sourceTokenAmount * sourceTokenPrice + const adjustedTotalSourceCostUSD = totalSourceCostUSD + _gasPriceInUSD + const effectivePriceIncludingGas = adjustedTotalSourceCostUSD / toTokenAmount + const priceImpactIncludingGas = + ((effectivePriceIncludingGas - toTokenPrice) / toTokenPrice) * 100 - return priceImpact + return priceImpactIncludingGas }, calculateMaxSlippage() { @@ -553,6 +562,13 @@ export const ConvertController = { throw new Error('>>> createConvert: Invalid values to start converting') } + const gasLimit = await ConnectionController.getEstimatedGas({ + address: fromAddress, + to: toTokenAddress, + data: '0x' + }) + state.gasPriceInUSD = this.calculateGasPriceInUSD(BigInt(gasLimit), state.gasFee) + const response = await ConvertApiController.getConvertData({ fromAddress, sourceTokenAddress, @@ -600,7 +616,7 @@ export const ConvertController = { const error = err as TransactionError state.transactionError = error?.shortMessage state.transactionLoading = false - SnackController.showError(error?.shortMessage || 'Transaction errror') + SnackController.showError(error?.shortMessage || 'Transaction error') return undefined } @@ -633,7 +649,7 @@ export const ConvertController = { state.toTokenPriceInUSD = parseFloat(toTokenPrice) state.gasPriceInUSD = this.calculateGasPriceInUSD(gas, gasPrice) - state.priceImpact = this.calculatePriceImpact(toTokenAmount) + state.priceImpact = this.calculatePriceImpact(toTokenAmount, state.gasPriceInUSD) state.maxSlippage = this.calculateMaxSlippage() } } diff --git a/packages/scaffold/src/views/w3m-convert-preview-view/index.ts b/packages/scaffold/src/views/w3m-convert-preview-view/index.ts index 2902acb6e3..e2e52b16c1 100644 --- a/packages/scaffold/src/views/w3m-convert-preview-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-preview-view/index.ts @@ -97,7 +97,8 @@ export class W3mConvertPreviewView extends LitElement { ?.symbol}` const sourceTokenValue = parseFloat(this.sourceTokenAmount) * this.sourceTokenPriceInUSD - const toTokenValue = parseFloat(this.toTokenAmount) * this.toTokenPriceInUSD + const toTokenValue = + parseFloat(this.toTokenAmount) * this.toTokenPriceInUSD - (this.gasPriceInUSD || 0) const sentPrice = formatNumberToLocalString(sourceTokenValue) const receivePrice = formatNumberToLocalString(toTokenValue) diff --git a/packages/scaffold/src/views/w3m-convert-view/index.ts b/packages/scaffold/src/views/w3m-convert-view/index.ts index b54e981510..46dc918267 100644 --- a/packages/scaffold/src/views/w3m-convert-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-view/index.ts @@ -20,6 +20,8 @@ export class W3mConvertView extends LitElement { private unsubscribe: ((() => void) | undefined)[] = [] // -- State & Properties -------------------------------- // + @state() private gasFeeIntervalId?: NodeJS.Timeout + @state() private detailsOpen = false @state() private caipNetworkId = NetworkController.state.caipNetwork?.id @@ -103,6 +105,8 @@ export class W3mConvertView extends LitElement { }) ] ) + + this.watchGasFee() } public override firstUpdated() { @@ -113,6 +117,7 @@ export class W3mConvertView extends LitElement { public override disconnectedCallback() { this.unsubscribe.forEach(unsubscribe => unsubscribe?.()) + clearInterval(this.gasFeeIntervalId) } // -- Render -------------------------------------------- // @@ -125,6 +130,10 @@ export class W3mConvertView extends LitElement { } // -- Private ------------------------------------------- // + private watchGasFee() { + this.gasFeeIntervalId = setInterval(() => ConvertController.setGasFee(), 5000) + } + private templateSwap() { return html` @@ -216,10 +225,32 @@ export class W3mConvertView extends LitElement { .token=${token} .balance=${myToken?.balance} .marketValue=${isNaN(value) ? '' : formatNumberToLocalString(value)} - amount=${myToken ? formatNumberToLocalString(myToken.balance, 3) : 0} + .onSetMaxValue=${this.onSetMaxValue.bind(this)} >` } + private onSetMaxValue(target: Target, balance: string | undefined) { + // Calculate the total cost of the gas in source tokens + let value = '0' + + if (!balance) { + value = '0' + this.handleChangeAmount(target, value) + return + } + + if (!this.gasPriceInUSD) { + value = balance + this.handleChangeAmount(target, value) + return + } + + const amountOfTokenGasRequires = this.gasPriceInUSD / this.sourceTokenPriceInUSD + const maxValue = parseFloat(balance) - amountOfTokenGasRequires + + this.handleChangeAmount(target, maxValue > 0 ? maxValue.toString() : '0') + } + private templateDetails() { if (this.loading || this.inputError) { return null diff --git a/packages/ui/src/composites/wui-convert-details/index.ts b/packages/ui/src/composites/wui-convert-details/index.ts index 12656a2ea3..8d0e162773 100644 --- a/packages/ui/src/composites/wui-convert-details/index.ts +++ b/packages/ui/src/composites/wui-convert-details/index.ts @@ -73,7 +73,7 @@ export class WuiConvertDetails extends LitElement { Price impact - ${formatNumberToLocalString(this.priceImpact, 3)}% + ${formatNumberToLocalString(this.priceImpact, 4)}% diff --git a/packages/ui/src/composites/wui-convert-input/index.ts b/packages/ui/src/composites/wui-convert-input/index.ts index 04f944f7ad..c386097586 100644 --- a/packages/ui/src/composites/wui-convert-input/index.ts +++ b/packages/ui/src/composites/wui-convert-input/index.ts @@ -6,6 +6,7 @@ import '../../components/wui-text/index.js' import '../wui-transaction-visual/index.js' import { EventsController, RouterController } from '@web3modal/core' import styles from './styles.js' +import { formatNumberToLocalString } from '../../utils/NumberUtil.js' type Target = 'sourceToken' | 'toToken' @@ -28,12 +29,12 @@ export class WuiConvertInput extends LitElement { // -- State & Properties -------------------------------- // @property() public focused = false + @property() public balance: string | undefined + @property() public value?: string @property() public marketValue?: string = '$1.0345,00' - @property() public amount?: string - @property() public disabled?: boolean @property() public target: Target = 'sourceToken' @@ -42,6 +43,9 @@ export class WuiConvertInput extends LitElement { @property() public onSetAmount: ((target: Target, value: string) => void) | null = null + @property() public onSetMaxValue: ((target: Target, balance: string | undefined) => void) | null = + null + // -- Render -------------------------------------------- // public override render() { return html` @@ -116,9 +120,7 @@ export class WuiConvertInput extends LitElement { } private setMaxValueToInput() { - if (this.amount?.toString()) { - this.onSetAmount?.(this.target, this.amount?.toString()) - } + this.onSetMaxValue?.(this.target, this.balance) } private templateTokenSelectButton() { @@ -151,7 +153,11 @@ export class WuiConvertInput extends LitElement { ${this.token.symbol} - ${this.amount} + ${this.balance + ? html` + ${formatNumberToLocalString(this.balance, 3)} + ` + : null} ${this.target === 'sourceToken' ? html` - ${this.balance + ${haveBalance ? html` ${formatNumberToLocalString(this.balance, 3)} ` : null} ${this.target === 'sourceToken' - ? html` ` + ? haveBalance + ? html` ` + : html` ` : null} @@ -178,6 +192,10 @@ export class WuiConvertInput extends LitElement { target: this.target }) } + + private onBuyToken() { + RouterController.push('OnRampProviders') + } } declare global { diff --git a/packages/ui/src/composites/wui-token-list-item/index.ts b/packages/ui/src/composites/wui-token-list-item/index.ts index 67ec9c7fc6..f1e1d3d9ff 100644 --- a/packages/ui/src/composites/wui-token-list-item/index.ts +++ b/packages/ui/src/composites/wui-token-list-item/index.ts @@ -10,6 +10,7 @@ import { customElement } from '../../utils/WebComponentsUtil.js' import '../wui-icon-box/index.js' import styles from './styles.js' import { formatNumberToLocalString } from '../../utils/NumberUtil.js' +import { NumberUtil } from '@web3modal/common' @customElement('wui-token-list-item') export class WuiTokenListItem extends LitElement { @@ -28,17 +29,21 @@ export class WuiTokenListItem extends LitElement { // -- Render -------------------------------------------- // public override render() { + const value = NumberUtil.multiply(this.price, this.amount, 3)?.toFixed(3) + return html` ${this.visualTemplate()} ${this.name} - ${this.price && - this.amount && - html`$${(parseFloat(this.price) * parseFloat(this.amount)).toFixed(2)}`} + ${value + ? html` + + $${formatNumberToLocalString(value, 3)} + + ` + : null} ${this.symbol} From f07dfdfcca3ea3474800f991f4de3539ef7cf5f1 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Fri, 22 Mar 2024 15:47:33 +0300 Subject: [PATCH 32/96] refactor: update price impact calculation method --- .../core/src/controllers/ConvertController.ts | 29 ++++++++++--------- .../composites/wui-convert-details/index.ts | 2 +- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/packages/core/src/controllers/ConvertController.ts b/packages/core/src/controllers/ConvertController.ts index 908994b6ac..b52ea110e5 100644 --- a/packages/core/src/controllers/ConvertController.ts +++ b/packages/core/src/controllers/ConvertController.ts @@ -40,7 +40,6 @@ export interface ConvertControllerState { // Approval & Convert transaction states approvalTransaction: TransactionParams | undefined - convertLoading: boolean convertTransaction: TransactionParams | undefined transactionLoading?: boolean transactionError?: string @@ -105,7 +104,6 @@ const state = proxy({ // Approval & Convert transaction states approvalTransaction: undefined, - convertLoading: false, convertTransaction: undefined, transactionError: undefined, transactionLoading: false, @@ -392,20 +390,26 @@ export const ConvertController = { } }, - calculatePriceImpact(_toTokenAmount: string, _gasPriceInUSD: number = 0) { - const toTokenAmount = NumberUtil.bigNumber(_toTokenAmount) - const sourceTokenPrice = state.sourceTokenPriceInUSD - const toTokenPrice = state.toTokenPriceInUSD + calculatePriceImpact(_toTokenAmount: string, _gasPriceInUSD = 0) { + const { toTokenDecimals } = this.getParams() + const decimals = toTokenDecimals || 18 + + const toTokenAmount = NumberUtil.bigNumber(_toTokenAmount).dividedBy(10 ** decimals) - const totalSourceCostUSD = NumberUtil.multiply(state.sourceTokenAmount, sourceTokenPrice) + const totalSourceCostUSD = NumberUtil.bigNumber(state.sourceTokenAmount).multipliedBy( + state.sourceTokenPriceInUSD + ) const adjustedTotalSourceCostUSD = totalSourceCostUSD.plus(_gasPriceInUSD) - const effectivePriceIncludingGas = adjustedTotalSourceCostUSD.dividedBy(toTokenAmount) - const priceImpactIncludingGas = effectivePriceIncludingGas - .minus(toTokenPrice) - .dividedBy(toTokenPrice) + const effectivePricePerTargetToken = adjustedTotalSourceCostUSD.dividedBy( + toTokenAmount.toString() + ) + + const priceImpact = effectivePricePerTargetToken + .minus(state.toTokenPriceInUSD) + .dividedBy(state.toTokenPriceInUSD) .multipliedBy(100) - return priceImpactIncludingGas.toNumber() + return priceImpact.toNumber() }, calculateMaxSlippage() { @@ -422,7 +426,6 @@ export const ConvertController = { return } - state.convertLoading = true await this.makeChecks() }, diff --git a/packages/ui/src/composites/wui-convert-details/index.ts b/packages/ui/src/composites/wui-convert-details/index.ts index 8d0e162773..12656a2ea3 100644 --- a/packages/ui/src/composites/wui-convert-details/index.ts +++ b/packages/ui/src/composites/wui-convert-details/index.ts @@ -73,7 +73,7 @@ export class WuiConvertDetails extends LitElement { Price impact - ${formatNumberToLocalString(this.priceImpact, 4)}% + ${formatNumberToLocalString(this.priceImpact, 3)}% From 3cf32e31f828d88e22c845d41f1927ba09120f94 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Fri, 22 Mar 2024 16:22:05 +0300 Subject: [PATCH 33/96] chore: add suggested tokens list --- packages/core/src/controllers/ConvertController.ts | 11 +++++++++++ .../src/views/w3m-convert-select-token-view/index.ts | 5 +++-- packages/ui/src/composites/wui-token-button/styles.ts | 1 + 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/packages/core/src/controllers/ConvertController.ts b/packages/core/src/controllers/ConvertController.ts index b52ea110e5..8e07e9182c 100644 --- a/packages/core/src/controllers/ConvertController.ts +++ b/packages/core/src/controllers/ConvertController.ts @@ -60,6 +60,7 @@ export interface ConvertControllerState { // Tokens tokens?: Record + suggestedTokens?: Record popularTokens?: Record foundTokens?: TokenInfo[] myTokensWithBalance?: Record @@ -125,6 +126,7 @@ const state = proxy({ // Tokens tokens: undefined, popularTokens: undefined, + suggestedTokens: undefined, foundTokens: undefined, myTokensWithBalance: undefined, tokensPriceMap: {}, @@ -293,6 +295,15 @@ export const ConvertController = { } return limitedTokens }, {}) + state.suggestedTokens = Object.entries(res.tokens).reduce>( + (limitedTokens, [tokenAddress, tokenInfo]) => { + if (ConstantsUtil.POPULAR_TOKENS.includes(tokenInfo.symbol)) { + limitedTokens[tokenAddress] = tokenInfo + } + return limitedTokens + }, + {} + ) return state.tokens }, diff --git a/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts b/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts index fd113c6a3e..af4d666ec9 100644 --- a/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts @@ -123,6 +123,7 @@ export class W3mConvertSelectTokenView extends LitElement { return html` Date: Fri, 22 Mar 2024 16:30:38 +0300 Subject: [PATCH 34/96] chore: linter issues --- packages/common/src/utils/NumberUtil.ts | 1 + .../core/src/controllers/ConvertController.ts | 7 ++- .../scaffold/src/partials/w3m-header/index.ts | 2 +- .../src/views/w3m-convert-view/index.ts | 3 +- .../src/composites/wui-convert-input/index.ts | 47 ++++++++++++------- 5 files changed, 39 insertions(+), 21 deletions(-) diff --git a/packages/common/src/utils/NumberUtil.ts b/packages/common/src/utils/NumberUtil.ts index 135b9a6adf..436b950b89 100644 --- a/packages/common/src/utils/NumberUtil.ts +++ b/packages/common/src/utils/NumberUtil.ts @@ -37,6 +37,7 @@ export const NumberUtil = { */ multiply(a: BigNumber.Value | undefined, b: BigNumber.Value | undefined) { if (a === undefined || b === undefined) { + // eslint-disable-next-line new-cap return BigNumber(0) } diff --git a/packages/core/src/controllers/ConvertController.ts b/packages/core/src/controllers/ConvertController.ts index 8e07e9182c..a395f67676 100644 --- a/packages/core/src/controllers/ConvertController.ts +++ b/packages/core/src/controllers/ConvertController.ts @@ -293,6 +293,7 @@ export const ConvertController = { if (ConstantsUtil.POPULAR_TOKENS.includes(tokenInfo.symbol)) { limitedTokens[tokenAddress] = tokenInfo } + return limitedTokens }, {}) state.suggestedTokens = Object.entries(res.tokens).reduce>( @@ -300,6 +301,7 @@ export const ConvertController = { if (ConstantsUtil.POPULAR_TOKENS.includes(tokenInfo.symbol)) { limitedTokens[tokenAddress] = tokenInfo } + return limitedTokens }, {} @@ -376,7 +378,7 @@ export const ConvertController = { async refreshConvertValues() { const { fromAddress, toTokenDecimals, toTokenAddress } = this.getParams() - if (fromAddress && toTokenAddress && toTokenDecimals) { + if (fromAddress && toTokenAddress && toTokenDecimals && !state.loading) { const transaction = await this.getTransaction() this.setTransactionDetails(transaction) } @@ -484,6 +486,7 @@ export const ConvertController = { transaction = await this.createTokenAllowance() state.approvalTransaction = transaction } + return transaction }, @@ -524,7 +527,7 @@ export const ConvertController = { gas: gasLimit, gasPrice: BigInt(transaction.gasPrice), value: BigInt(transaction.value), - toAmount: toAmount + toAmount } }, diff --git a/packages/scaffold/src/partials/w3m-header/index.ts b/packages/scaffold/src/partials/w3m-header/index.ts index d5d802f6fc..8e855d7eed 100644 --- a/packages/scaffold/src/partials/w3m-header/index.ts +++ b/packages/scaffold/src/partials/w3m-header/index.ts @@ -56,7 +56,7 @@ function headings() { Convert: 'Convert', ConvertSelectToken: 'Select Token', ConvertSelectNetwork: 'Select Network', - ConvertPreview: 'Preview Convert' + ConvertPreview: 'Preview Convert', WalletSend: 'Send', WalletSendPreview: 'Review send', WalletSendSelectToken: 'Select Token' diff --git a/packages/scaffold/src/views/w3m-convert-view/index.ts b/packages/scaffold/src/views/w3m-convert-view/index.ts index 60c474504b..5e3383221c 100644 --- a/packages/scaffold/src/views/w3m-convert-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-view/index.ts @@ -230,18 +230,19 @@ export class W3mConvertView extends LitElement { } private onSetMaxValue(target: Target, balance: string | undefined) { - // Calculate the total cost of the gas in source tokens let value = '0' if (!balance) { value = '0' this.handleChangeAmount(target, value) + return } if (!this.gasPriceInUSD) { value = balance this.handleChangeAmount(target, value) + return } diff --git a/packages/ui/src/composites/wui-convert-input/index.ts b/packages/ui/src/composites/wui-convert-input/index.ts index 6efb8469eb..7fd2a888bf 100644 --- a/packages/ui/src/composites/wui-convert-input/index.ts +++ b/packages/ui/src/composites/wui-convert-input/index.ts @@ -36,7 +36,7 @@ export class WuiConvertInput extends LitElement { @property() public value?: string - @property() public price: number = 0 + @property() public price = 0 @property() public marketValue?: string = '$1.0345,00' @@ -162,26 +162,39 @@ export class WuiConvertInput extends LitElement { ${tokenElement} ${this.token.symbol} - - ${haveBalance - ? html` - ${formatNumberToLocalString(this.balance, 3)} - ` - : null} - ${this.target === 'sourceToken' - ? haveBalance - ? html` ` - : html` ` - : null} - + ${this.tokenBalanceTemplate()} ` } + private tokenBalanceTemplate() { + const balanceValueInUSD = NumberUtil.multiply(this.balance, this.price) + const haveBalance = balanceValueInUSD + ? balanceValueInUSD?.isGreaterThan(MINIMUM_USD_VALUE_TO_CONVERT) + : false + + return html` + ${haveBalance + ? html` + ${formatNumberToLocalString(this.balance, 3)} + ` + : null} + ${this.target === 'sourceToken' ? this.tokenActionButtonTemplate(haveBalance) : null} + ` + } + + private tokenActionButtonTemplate(_haveBalance: boolean) { + if (_haveBalance) { + return html` ` + } + + return html` ` + } + private onFocusChange(state: boolean) { this.focused = state } From c63f4e61ee84dd5600d6a310329479c066a646a1 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Fri, 22 Mar 2024 16:35:21 +0300 Subject: [PATCH 35/96] fix: build and linter issues --- packages/ui/src/composites/wui-convert-input/index.ts | 5 ----- packages/ui/src/composites/wui-token-list-item/index.ts | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/ui/src/composites/wui-convert-input/index.ts b/packages/ui/src/composites/wui-convert-input/index.ts index 7fd2a888bf..d43d203de5 100644 --- a/packages/ui/src/composites/wui-convert-input/index.ts +++ b/packages/ui/src/composites/wui-convert-input/index.ts @@ -135,11 +135,6 @@ export class WuiConvertInput extends LitElement { ` } - const balanceValueInUSD = NumberUtil.multiply(this.balance, this.price) - const haveBalance = balanceValueInUSD - ? balanceValueInUSD?.isGreaterThan(MINIMUM_USD_VALUE_TO_CONVERT) - : false - const tokenElement = this.token.logoURI ? html`` : html` diff --git a/packages/ui/src/composites/wui-token-list-item/index.ts b/packages/ui/src/composites/wui-token-list-item/index.ts index f1e1d3d9ff..5e8f4d67df 100644 --- a/packages/ui/src/composites/wui-token-list-item/index.ts +++ b/packages/ui/src/composites/wui-token-list-item/index.ts @@ -29,7 +29,7 @@ export class WuiTokenListItem extends LitElement { // -- Render -------------------------------------------- // public override render() { - const value = NumberUtil.multiply(this.price, this.amount, 3)?.toFixed(3) + const value = NumberUtil.multiply(this.price, this.amount)?.toFixed(3) return html` From 8c3d9156fedf419469f198f51ee8ad6419d410dc Mon Sep 17 00:00:00 2001 From: enesozturk Date: Thu, 28 Mar 2024 05:23:30 +0300 Subject: [PATCH 36/96] feat: add new ui components for different variations of transaction UIs --- .../composites/wui-details-group.stories.ts | 27 ++++++++++++++++ .../views/w3m-convert-preview-view/index.ts | 4 ++- packages/ui/index.ts | 2 ++ packages/ui/src/components/wui-image/index.ts | 8 +++++ .../ui/src/components/wui-image/styles.ts | 4 +-- .../wui-details-group-item/index.ts | 32 +++++++++++++++++++ .../wui-details-group-item/styles.ts | 11 +++++++ .../src/composites/wui-details-group/index.ts | 25 +++++++++++++++ .../composites/wui-details-group/styles.ts | 10 ++++++ .../ui/src/composites/wui-list-token/index.ts | 9 +++--- packages/ui/src/utils/JSXTypeUtil.ts | 4 +++ packages/ui/src/utils/UiHelperUtil.ts | 23 +++++++++++++ 12 files changed, 151 insertions(+), 8 deletions(-) create mode 100644 apps/gallery/stories/composites/wui-details-group.stories.ts create mode 100644 packages/ui/src/composites/wui-details-group-item/index.ts create mode 100644 packages/ui/src/composites/wui-details-group-item/styles.ts create mode 100644 packages/ui/src/composites/wui-details-group/index.ts create mode 100644 packages/ui/src/composites/wui-details-group/styles.ts diff --git a/apps/gallery/stories/composites/wui-details-group.stories.ts b/apps/gallery/stories/composites/wui-details-group.stories.ts new file mode 100644 index 0000000000..6b917dbff3 --- /dev/null +++ b/apps/gallery/stories/composites/wui-details-group.stories.ts @@ -0,0 +1,27 @@ +import type { Meta } from '@storybook/web-components' +import '@web3modal/ui/src/composites/wui-cta-button' +import type { WuiDetailsGroup } from '@web3modal/ui/src/composites/wui-details-group' +import { html } from 'lit' +import '../../components/gallery-container' + +type Component = Meta + +export default { + title: 'Composites/wui-details-group', + args: {} +} as Component + +export const Default: Component = { + render: () => html` + + + + 2 AVAX + + + 0x276...f0ed7 + + + + ` +} diff --git a/packages/scaffold/src/views/w3m-convert-preview-view/index.ts b/packages/scaffold/src/views/w3m-convert-preview-view/index.ts index e2e52b16c1..2ffd4c1506 100644 --- a/packages/scaffold/src/views/w3m-convert-preview-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-preview-view/index.ts @@ -5,7 +5,8 @@ import { AccountController, NetworkController, RouterController, - ConvertController + ConvertController, + ModalController } from '@web3modal/core' import { state } from 'lit/decorators.js' @@ -228,6 +229,7 @@ export class W3mConvertPreviewView extends LitElement { } private onSendTransaction() { + ModalController.close() if (this.approvalTransaction) { ConvertController.sendTransactionForApproval(this.approvalTransaction) } else { diff --git a/packages/ui/index.ts b/packages/ui/index.ts index e5c18200b9..023ab9edf8 100644 --- a/packages/ui/index.ts +++ b/packages/ui/index.ts @@ -18,6 +18,8 @@ export * from './src/composites/wui-chip/index.js' export * from './src/composites/wui-connect-button/index.js' export * from './src/composites/wui-convert-details/index.js' export * from './src/composites/wui-cta-button/index.js' +export * from './src/composites/wui-details-group/index.js' +export * from './src/composites/wui-details-group-item/index.js' export * from './src/composites/wui-email-input/index.js' export * from './src/composites/wui-icon-box/index.js' export * from './src/composites/wui-icon-link/index.js' diff --git a/packages/ui/src/components/wui-image/index.ts b/packages/ui/src/components/wui-image/index.ts index 4593355441..4237e81ec1 100644 --- a/packages/ui/src/components/wui-image/index.ts +++ b/packages/ui/src/components/wui-image/index.ts @@ -3,6 +3,7 @@ import { property } from 'lit/decorators.js' import { colorStyles, resetStyles } from '../../utils/ThemeUtil.js' import { customElement } from '../../utils/WebComponentsUtil.js' import styles from './styles.js' +import type { SizeType } from '../../utils/TypeUtil.js' @customElement('wui-image') export class WuiImage extends LitElement { @@ -13,8 +14,15 @@ export class WuiImage extends LitElement { @property() public alt = 'Image' + @property() public size?: SizeType = undefined + // -- Render -------------------------------------------- // public override render() { + this.style.cssText = ` + --local-width: ${this.size ? `var(--wui-icon-size-${this.size});` : '100%'}; + --local-height: ${this.size ? `var(--wui-icon-size-${this.size});` : '100%'}; + ` + return html`${this.alt}` } } diff --git a/packages/ui/src/components/wui-image/styles.ts b/packages/ui/src/components/wui-image/styles.ts index de81aad1f3..3aa19a01ad 100644 --- a/packages/ui/src/components/wui-image/styles.ts +++ b/packages/ui/src/components/wui-image/styles.ts @@ -3,8 +3,8 @@ import { css } from 'lit' export default css` :host { display: block; - width: 100%; - height: 100%; + width: var(--local-width); + height: var(--local-height); } img { diff --git a/packages/ui/src/composites/wui-details-group-item/index.ts b/packages/ui/src/composites/wui-details-group-item/index.ts new file mode 100644 index 0000000000..12e95c9227 --- /dev/null +++ b/packages/ui/src/composites/wui-details-group-item/index.ts @@ -0,0 +1,32 @@ +import { html, LitElement } from 'lit' +import { property } from 'lit/decorators.js' +import '../../layout/wui-flex/index.js' +import { elementStyles, resetStyles } from '../../utils/ThemeUtil.js' +import { customElement } from '../../utils/WebComponentsUtil.js' +import styles from './styles.js' + +@customElement('wui-details-group-item') +export class WuiDetailsGroupItem extends LitElement { + public static override styles = [resetStyles, elementStyles, styles] + + // -- State & Properties -------------------------------- // + @property() public name: string = '' + + // -- Render -------------------------------------------- // + public override render() { + return html` + + ${this.name} + + + + + ` + } +} + +declare global { + interface HTMLElementTagNameMap { + 'wui-details-group-item': WuiDetailsGroupItem + } +} diff --git a/packages/ui/src/composites/wui-details-group-item/styles.ts b/packages/ui/src/composites/wui-details-group-item/styles.ts new file mode 100644 index 0000000000..188c5c0ccd --- /dev/null +++ b/packages/ui/src/composites/wui-details-group-item/styles.ts @@ -0,0 +1,11 @@ +import { css } from 'lit' + +export default css` + :host { + display: flex; + flex-direction: row; + gap: var(--wui-spacing-l); + width: 100%; + border-radius: var(--wui-border-radius-xs); + } +` diff --git a/packages/ui/src/composites/wui-details-group/index.ts b/packages/ui/src/composites/wui-details-group/index.ts new file mode 100644 index 0000000000..480934391e --- /dev/null +++ b/packages/ui/src/composites/wui-details-group/index.ts @@ -0,0 +1,25 @@ +import { html, LitElement } from 'lit' +import '../../layout/wui-flex/index.js' +import { elementStyles, resetStyles } from '../../utils/ThemeUtil.js' +import { customElement } from '../../utils/WebComponentsUtil.js' +import styles from './styles.js' + +@customElement('wui-details-group') +export class WuiDetailsGroup extends LitElement { + public static override styles = [resetStyles, elementStyles, styles] + + // -- Render -------------------------------------------- // + public override render() { + return html` + + + + ` + } +} + +declare global { + interface HTMLElementTagNameMap { + 'wui-details-group': WuiDetailsGroup + } +} diff --git a/packages/ui/src/composites/wui-details-group/styles.ts b/packages/ui/src/composites/wui-details-group/styles.ts new file mode 100644 index 0000000000..9995b03b71 --- /dev/null +++ b/packages/ui/src/composites/wui-details-group/styles.ts @@ -0,0 +1,10 @@ +import { css } from 'lit' + +export default css` + :host { + padding: var(--wui-spacing-l) var(--wui-spacing-m); + background-color: var(--wui-gray-glass-002); + border-radius: var(--wui-border-radius-xs); + width: 100%; + } +` diff --git a/packages/ui/src/composites/wui-list-token/index.ts b/packages/ui/src/composites/wui-list-token/index.ts index fe1101a914..74e2c4027f 100644 --- a/packages/ui/src/composites/wui-list-token/index.ts +++ b/packages/ui/src/composites/wui-list-token/index.ts @@ -33,11 +33,10 @@ export class WuiListToken extends LitElement { ${this.tokenName} - ${UiHelperUtil.roundNumber(Number(this.tokenAmount), 6, 5)} - ${this.tokenCurrency} + + ${UiHelperUtil.formatNumberToLocalString(this.tokenAmount, 4)} ${this.tokenCurrency} + + $${this.tokenValue.toFixed(2)} diff --git a/packages/ui/src/utils/JSXTypeUtil.ts b/packages/ui/src/utils/JSXTypeUtil.ts index 20f8215b7b..57dec7391a 100644 --- a/packages/ui/src/utils/JSXTypeUtil.ts +++ b/packages/ui/src/utils/JSXTypeUtil.ts @@ -17,6 +17,8 @@ import type { WuiCardSelect } from '../composites/wui-card-select/index.js' import type { WuiChip } from '../composites/wui-chip/index.js' import type { WuiConnectButton } from '../composites/wui-connect-button/index.js' import type { WuiCtaButton } from '../composites/wui-cta-button/index.js' +import type { WuiDetailsGroup } from '../composites/wui-details-group/index.js' +import type { WuiDetailsGroupItem } from '../composites/wui-details-group-item/index.js' import type { WuiEmailInput } from '../composites/wui-email-input/index.js' import type { WuiIconBox } from '../composites/wui-icon-box/index.js' import type { WuiIconLink } from '../composites/wui-icon-link/index.js' @@ -87,6 +89,8 @@ declare global { 'wui-chip': CustomElement 'wui-connect-button': CustomElement 'wui-cta-button': CustomElement + 'wui-details-group': CustomElement + 'wui-details-group-item': CustomElement 'wui-email-input': CustomElement 'wui-icon-box': CustomElement 'wui-icon-link': CustomElement diff --git a/packages/ui/src/utils/UiHelperUtil.ts b/packages/ui/src/utils/UiHelperUtil.ts index 7c09e82a57..7d399687c1 100644 --- a/packages/ui/src/utils/UiHelperUtil.ts +++ b/packages/ui/src/utils/UiHelperUtil.ts @@ -119,5 +119,28 @@ export const UiHelperUtil = { const roundedNumber = Math.abs(number) >= threshold ? Number(number.toFixed(fixed)) : number return roundedNumber + }, + /** + * Format the given number or string to human readable numbers with the given number of decimals + * @param value - The value to format. It could be a number or string. If it's a string, it will be parsed to a float then formatted. + * @param decimals - number of decimals after dot + * @returns + */ + formatNumberToLocalString(value: string | number | undefined, decimals = 2) { + if (value === undefined) { + return '0.00' + } + + if (typeof value === 'number') { + return value.toLocaleString('en-US', { + maximumFractionDigits: decimals, + minimumFractionDigits: decimals + }) + } + + return parseFloat(value).toLocaleString('en-US', { + maximumFractionDigits: decimals, + minimumFractionDigits: decimals + }) } } From a80ec5034e2770aa728808fa81a01f5521e93d40 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Mon, 1 Apr 2024 21:03:55 +0300 Subject: [PATCH 37/96] feat: ui/styling improvements --- .../scaffold/src/partials/w3m-header/index.ts | 6 +- .../views/w3m-convert-preview-view/index.ts | 27 ++----- .../views/w3m-convert-preview-view/styles.ts | 5 ++ .../w3m-convert-select-token-view/styles.ts | 7 ++ .../src/views/w3m-convert-view/index.ts | 46 +++++------- .../src/views/w3m-convert-view/styles.ts | 16 ++-- .../ui/src/composites/wui-button/index.ts | 2 - .../composites/wui-convert-details/index.ts | 2 +- .../composites/wui-convert-details/styles.ts | 9 +-- .../src/composites/wui-convert-input/index.ts | 75 ++++++++++++++++++- .../composites/wui-convert-input/styles.ts | 33 ++++++-- packages/wagmi/src/client.ts | 32 +++++++- 12 files changed, 176 insertions(+), 84 deletions(-) diff --git a/packages/scaffold/src/partials/w3m-header/index.ts b/packages/scaffold/src/partials/w3m-header/index.ts index 8e855d7eed..160e6d5557 100644 --- a/packages/scaffold/src/partials/w3m-header/index.ts +++ b/packages/scaffold/src/partials/w3m-header/index.ts @@ -54,9 +54,9 @@ function headings() { WalletReceive: 'Receive', WalletCompatibleNetworks: 'Compatible Networks', Convert: 'Convert', - ConvertSelectToken: 'Select Token', - ConvertSelectNetwork: 'Select Network', - ConvertPreview: 'Preview Convert', + ConvertSelectToken: 'Select token', + ConvertSelectNetwork: 'Select network', + ConvertPreview: 'Preview convert', WalletSend: 'Send', WalletSendPreview: 'Review send', WalletSendSelectToken: 'Select Token' diff --git a/packages/scaffold/src/views/w3m-convert-preview-view/index.ts b/packages/scaffold/src/views/w3m-convert-preview-view/index.ts index 2ffd4c1506..ef6775d60e 100644 --- a/packages/scaffold/src/views/w3m-convert-preview-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-preview-view/index.ts @@ -5,8 +5,7 @@ import { AccountController, NetworkController, RouterController, - ConvertController, - ModalController + ConvertController } from '@web3modal/core' import { state } from 'lit/decorators.js' @@ -123,22 +122,7 @@ export class W3mConvertPreviewView extends LitElement { > - - - - - + ${this.transactionLoading ? html`` - : html`${this.actionButtonLabel()}`} + : html` + ${this.actionButtonLabel()} + `} @@ -229,7 +213,6 @@ export class W3mConvertPreviewView extends LitElement { } private onSendTransaction() { - ModalController.close() if (this.approvalTransaction) { ConvertController.sendTransactionForApproval(this.approvalTransaction) } else { diff --git a/packages/scaffold/src/views/w3m-convert-preview-view/styles.ts b/packages/scaffold/src/views/w3m-convert-preview-view/styles.ts index 053c9ba862..15ac06bb45 100644 --- a/packages/scaffold/src/views/w3m-convert-preview-view/styles.ts +++ b/packages/scaffold/src/views/w3m-convert-preview-view/styles.ts @@ -74,6 +74,11 @@ export default css` cursor: not-allowed; } + .cancel-button:hover, + .convert-button:hover { + cursor: pointer; + } + .action-buttons-container > button.cancel-button { flex: 2; } diff --git a/packages/scaffold/src/views/w3m-convert-select-token-view/styles.ts b/packages/scaffold/src/views/w3m-convert-select-token-view/styles.ts index b5aac803f8..5b60af21ee 100644 --- a/packages/scaffold/src/views/w3m-convert-select-token-view/styles.ts +++ b/packages/scaffold/src/views/w3m-convert-select-token-view/styles.ts @@ -1,6 +1,13 @@ import { css } from 'lit' export default css` + :host { + --tokens-scroll--top-opacity: 0; + --tokens-scroll--bottom-opacity: 1; + --suggested-tokens-scroll--left-opacity: 0; + --suggested-tokens-scroll--right-opacity: 1; + } + :host > wui-flex:first-child { overflow-y: hidden; overflow-x: hidden; diff --git a/packages/scaffold/src/views/w3m-convert-view/index.ts b/packages/scaffold/src/views/w3m-convert-view/index.ts index 5e3383221c..6201da4c22 100644 --- a/packages/scaffold/src/views/w3m-convert-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-view/index.ts @@ -7,10 +7,11 @@ import { RouterController, CoreHelperUtil, NetworkController, - ModalController + ModalController, + ConstantsUtil } from '@web3modal/core' -import type { TokenInfo } from '@web3modal/core/src/controllers/ConvertController.js' import { NumberUtil } from '@web3modal/common' +import type { TokenInfo } from '@web3modal/core/src/controllers/ConvertApiController.js' type Target = 'sourceToken' | 'toToken' @@ -115,6 +116,7 @@ export class W3mConvertView extends LitElement { } public override disconnectedCallback() { + ConvertController.setLoading(false) this.unsubscribe.forEach(unsubscribe => unsubscribe?.()) clearInterval(this.gasFeeIntervalId) } @@ -122,7 +124,7 @@ export class W3mConvertView extends LitElement { // -- Render -------------------------------------------- // public override render() { return html` - + ${this.initialized ? this.templateSwap() : this.templateLoading()} ` @@ -135,7 +137,7 @@ export class W3mConvertView extends LitElement { private templateSwap() { return html` - + - -
+ ` } @@ -229,7 +214,10 @@ export class W3mConvertView extends LitElement { >` } - private onSetMaxValue(target: Target, balance: string | undefined) { + private onSetMaxValue(target: Target, balance: string | undefined, address: string | undefined) { + const token = target === 'sourceToken' ? this.sourceToken : this.toToken + const isNetworkToken = token?.address === ConstantsUtil.NATIVE_TOKEN_ADDRESS + let value = '0' if (!balance) { @@ -246,8 +234,12 @@ export class W3mConvertView extends LitElement { return } - const amountOfTokenGasRequires = this.gasPriceInUSD / this.sourceTokenPriceInUSD - const maxValue = NumberUtil.bigNumber(balance).minus(amountOfTokenGasRequires) + const amountOfTokenGasRequires = NumberUtil.bigNumber(this.gasPriceInUSD.toFixed(5)).dividedBy( + this.sourceTokenPriceInUSD + ) + const maxValue = isNetworkToken + ? NumberUtil.bigNumber(balance).minus(amountOfTokenGasRequires) + : NumberUtil.bigNumber(balance) this.handleChangeAmount(target, maxValue.isGreaterThan(0) ? maxValue.toFixed(20) : '0') } diff --git a/packages/scaffold/src/views/w3m-convert-view/styles.ts b/packages/scaffold/src/views/w3m-convert-view/styles.ts index 75e1a07b9a..408f50a270 100644 --- a/packages/scaffold/src/views/w3m-convert-view/styles.ts +++ b/packages/scaffold/src/views/w3m-convert-view/styles.ts @@ -24,23 +24,15 @@ export default css` position: relative; } - .replace-tokens-button-container { + .replace-tokens-button { display: flex; justify-content: center; align-items: center; + cursor: pointer; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); - background: var(--wui-color-modal-bg); - border-radius: calc(var(--wui-border-radius-s) - 2px); - padding: 5px; - } - - .replace-tokens-button { - display: flex; - justify-content: center; - align-items: center; gap: var(--wui-spacing-1xs); height: 40px; width: 40px; @@ -48,11 +40,13 @@ export default css` border: none; border-radius: var(--wui-border-radius-xxs); background: var(--wui-gray-glass-005); + transition: background-color var(--wui-duration-md) var(--wui-ease-out-power-1); + will-change: background-color; + z-index: 20; } .replace-tokens-button:hover { background: var(--wui-gray-glass-010); - cursor: pointer; } .details-container > wui-flex { diff --git a/packages/ui/src/composites/wui-button/index.ts b/packages/ui/src/composites/wui-button/index.ts index 624730114b..1365b95d9f 100644 --- a/packages/ui/src/composites/wui-button/index.ts +++ b/packages/ui/src/composites/wui-button/index.ts @@ -42,8 +42,6 @@ export class WuiButton extends LitElement { public override render() { this.style.cssText = ` --local-width: ${this.fullWidth ? '100%' : 'auto'}; - --local-border-color: ${this.fullWidth ? '100%' : 'auto'}; - --local-background-color: ${this.fullWidth ? '100%' : 'auto'}; --local-opacity-100: ${this.loading ? 0 : 1}; --local-opacity-000: ${this.loading ? 1 : 0}; --local-border-radius: var(--wui-border-radius-${this.borderRadius}); diff --git a/packages/ui/src/composites/wui-convert-details/index.ts b/packages/ui/src/composites/wui-convert-details/index.ts index 12656a2ea3..7de2390202 100644 --- a/packages/ui/src/composites/wui-convert-details/index.ts +++ b/packages/ui/src/composites/wui-convert-details/index.ts @@ -31,7 +31,7 @@ export class WuiConvertDetails extends LitElement { // -- Render -------------------------------------------- // public override render() { return html` - + ) diff --git a/apps/laboratory/src/components/Ethers/EthersSignTypedDataTest.tsx b/apps/laboratory/src/components/Ethers/EthersSignTypedDataTest.tsx index 1c91393a49..a087afb0b1 100644 --- a/apps/laboratory/src/components/Ethers/EthersSignTypedDataTest.tsx +++ b/apps/laboratory/src/components/Ethers/EthersSignTypedDataTest.tsx @@ -1,4 +1,5 @@ -import { Button, useToast } from '@chakra-ui/react' +import { Button } from '@/components/ui/button' +import { useToast } from '@chakra-ui/react' import { useWeb3ModalAccount, useWeb3ModalProvider } from '@web3modal/ethers/react' import { BrowserProvider, JsonRpcSigner } from 'ethers' import type { TypedDataField } from 'ethers' @@ -60,7 +61,7 @@ export function EthersSignTypedDataTest() { } return ( - ) diff --git a/apps/laboratory/src/components/Ethers/EthersTests.tsx b/apps/laboratory/src/components/Ethers/EthersTests.tsx index 493d6ab283..8a75d739d7 100644 --- a/apps/laboratory/src/components/Ethers/EthersTests.tsx +++ b/apps/laboratory/src/components/Ethers/EthersTests.tsx @@ -1,49 +1,38 @@ import { useWeb3ModalAccount } from '@web3modal/ethers/react' import { EthersSignMessageTest } from './EthersSignMessageTest' import { EthersSignTypedDataTest } from './EthersSignTypedDataTest' -import { StackDivider, Card, CardHeader, Heading, CardBody, Box, Stack } from '@chakra-ui/react' import { EthersTransactionTest } from './EthersTransactionTest' import { EthersWriteContractTest } from './EthersWriteContractTest' +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' +import { Column } from '@/components/ui/column' +import { Span } from '@/components/ui/typography' export function EthersTests() { const { isConnected } = useWeb3ModalAccount() return isConnected ? ( - - - Test Interactions + + + Test Interactions - - - } spacing="4"> - - - Sign Message - - - - - - - Sign Typed Data - - - - - - - Sign Transaction - - - - - - Contract Write - - - - - + + + Sign Message + + + + Sign Typed Data + + + + Sign Transaction + + + + Contract Write + + + ) : null } diff --git a/apps/laboratory/src/components/Ethers/EthersTransactionTest.tsx b/apps/laboratory/src/components/Ethers/EthersTransactionTest.tsx index fe3c347d96..d1f8ca2e16 100644 --- a/apps/laboratory/src/components/Ethers/EthersTransactionTest.tsx +++ b/apps/laboratory/src/components/Ethers/EthersTransactionTest.tsx @@ -1,9 +1,14 @@ -import { Button, useToast, Stack, Link, Text, Spacer } from '@chakra-ui/react' +import { useToast, Link } from '@chakra-ui/react' import { useWeb3ModalAccount, useWeb3ModalProvider } from '@web3modal/ethers/react' import { BrowserProvider, JsonRpcSigner, ethers } from 'ethers' import { sepolia, optimism } from '../../utils/ChainsUtil' import { useState } from 'react' import { vitalikEthAddress } from '../../utils/DataUtil' +import { Button, buttonVariants } from '@/components/ui/button' +import { cn } from '@/lib/utils' +import { Row } from '@/components/ui/row' +import { Column } from '@/components/ui/column' +import { Span } from '@/components/ui/typography' export function EthersTransactionTest() { const toast = useToast() @@ -39,32 +44,37 @@ export function EthersTransactionTest() { const allowedChains = [sepolia.chainId, optimism.chainId] return allowedChains.includes(Number(chainId)) && address ? ( - + - - - - - + - - - - + + + ) : ( - + Switch to Sepolia or OP to test this feature - + ) } diff --git a/apps/laboratory/src/components/Ethers/EthersWriteContractTest.tsx b/apps/laboratory/src/components/Ethers/EthersWriteContractTest.tsx index 760e5b0bc2..4ff384c49d 100644 --- a/apps/laboratory/src/components/Ethers/EthersWriteContractTest.tsx +++ b/apps/laboratory/src/components/Ethers/EthersWriteContractTest.tsx @@ -1,10 +1,16 @@ -import { Button, useToast, Stack, Link, Text, Spacer } from '@chakra-ui/react' +import { useToast } from '@chakra-ui/react' import { useWeb3ModalAccount, useWeb3ModalProvider } from '@web3modal/ethers/react' import { BrowserProvider, JsonRpcSigner, ethers } from 'ethers' import { optimism, sepolia } from '../../utils/ChainsUtil' import { useState } from 'react' import { abi, address as donutAddress } from '../../utils/DonutContract' +import { Column } from '@/components/ui/column' +import { Button, buttonVariants } from '@/components/ui/button' +import { Row } from '@/components/ui/row' +import Link from 'next/link' +import { cn } from '@/lib/utils' +import { Span } from '@/components/ui/typography' export function EthersWriteContractTest() { const toast = useToast() @@ -38,32 +44,37 @@ export function EthersWriteContractTest() { const allowedChains = [sepolia.chainId, optimism.chainId] return allowedChains.includes(Number(chainId)) && address ? ( - + - - - - - + - - - - + + + ) : ( - + Switch to Sepolia or OP to test this feature - + ) } diff --git a/apps/laboratory/src/components/Siwe/SiweData.tsx b/apps/laboratory/src/components/Siwe/SiweData.tsx index 57cfbf00cf..5ec88bab35 100644 --- a/apps/laboratory/src/components/Siwe/SiweData.tsx +++ b/apps/laboratory/src/components/Siwe/SiweData.tsx @@ -1,16 +1,9 @@ -import { - StackDivider, - Card, - CardHeader, - Heading, - CardBody, - Box, - Stack, - Text -} from '@chakra-ui/react' import { useEffect, useState } from 'react' import { useSession } from 'next-auth/react' import type { SIWESession } from '@web3modal/siwe' +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' +import { Column } from '@/components/ui/column' +import { Span } from '@/components/ui/typography' export function SiweData() { const [ready, setReady] = useState(false) @@ -22,41 +15,27 @@ export function SiweData() { }, []) return ready ? ( - - - SIWE Session Details + + + SIWE Session Details + + + + Session Status + {status} + - - } spacing="4"> - - - Session Status - - - {status} - - - - - - Session Network - - - {`eip155:${session?.chainId}`} - - - - - - Session Network Address - - - {session?.address} - - - - + + Session Network + {`eip155:${session?.chainId}`} + + + Session Network Address + {session?.address || '-'} + + + ) : null } diff --git a/apps/laboratory/src/components/Solana/SolanaSignMessageTest.tsx b/apps/laboratory/src/components/Solana/SolanaSignMessageTest.tsx index fe74089b80..ec15c427c1 100644 --- a/apps/laboratory/src/components/Solana/SolanaSignMessageTest.tsx +++ b/apps/laboratory/src/components/Solana/SolanaSignMessageTest.tsx @@ -1,8 +1,9 @@ -import { Button, useToast } from '@chakra-ui/react' +import { useToast } from '@chakra-ui/react' import { useWeb3ModalAccount, useWeb3ModalProvider } from '@web3modal/solana/react' import { ConstantsUtil } from '../../utils/ConstantsUtil' +import { Button } from '@/components/ui/button' export function SolanaSignMessageTest() { const toast = useToast() @@ -46,7 +47,7 @@ export function SolanaSignMessageTest() { } return ( - ) diff --git a/apps/laboratory/src/components/Solana/SolanaTests.tsx b/apps/laboratory/src/components/Solana/SolanaTests.tsx index 8b4f94989b..b68e8f8d89 100644 --- a/apps/laboratory/src/components/Solana/SolanaTests.tsx +++ b/apps/laboratory/src/components/Solana/SolanaTests.tsx @@ -1,58 +1,42 @@ import { useWeb3ModalAccount } from '@web3modal/solana/react' -import { - StackDivider, - Card, - CardHeader, - Heading, - CardBody, - Box, - Stack, - Text -} from '@chakra-ui/react' import { SolanaSignTransactionTest } from './SolanaSignTransactionTest' import { SolanaSendTransactionTest } from './SolanaSendTransactionTest' -import { SolanaSignMessageTest } from './SolanaSignMessageTest' import { solana } from '../../utils/ChainsUtil' +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' +import { Span } from '@/components/ui/typography' +import { Column } from '@/components/ui/column' + export function SolanaTests() { const { isConnected, currentChain } = useWeb3ModalAccount() return isConnected ? ( - - - Test Interactions + + + Test Interactions - - - } spacing="4"> - - - Sign Message - - - - {currentChain?.chainId !== solana.chainId && ( - - + + + + Sign Message + {currentChain?.chainId !== solana.chainId && ( + Please ensure your wallet is connected to the {currentChain?.name} - - - )} - - - Sign Transaction - + + )} + + + + Sign Transaction - - - - Sign and Send Transaction - + + + Sign and Send Transaction - - - + + + ) : null } diff --git a/apps/laboratory/src/components/Theming/AccentColorInput.tsx b/apps/laboratory/src/components/Theming/AccentColorInput.tsx index 819982a5e2..ee3023b265 100644 --- a/apps/laboratory/src/components/Theming/AccentColorInput.tsx +++ b/apps/laboratory/src/components/Theming/AccentColorInput.tsx @@ -26,7 +26,6 @@ export default function AccentColorInput() { {colors.map(value => { const radio = getRadioProps({ value }) - return ( {value} diff --git a/apps/laboratory/src/components/Theming/BorderRadiusInput.tsx b/apps/laboratory/src/components/Theming/BorderRadiusInput.tsx index 17f2e0a011..a678f7c352 100644 --- a/apps/laboratory/src/components/Theming/BorderRadiusInput.tsx +++ b/apps/laboratory/src/components/Theming/BorderRadiusInput.tsx @@ -1,48 +1,30 @@ -import { - Heading, - Slider, - SliderFilledTrack, - SliderMark, - SliderThumb, - SliderTrack -} from '@chakra-ui/react' - import { ThemeStore } from '../../utils/StoreUtil' import { useProxy } from 'valtio/utils' +import { cn } from '@/lib/utils' +import { Span } from '@/components/ui/typography' +import { Row } from '@/components/ui/row' +import { Slider } from '@/components/ui/slider' export default function BorderRadiusInput() { const state = useProxy(ThemeStore.state) return ( <> - - Border Radius - - { - ThemeStore.setBorderRadius(`${val}px`) - }} - > - - {state.borderRadius} - - - - - - + Border Radius + + + { + ThemeStore.setBorderRadius(`${value}px`) + }} + /> + {state.borderRadius} + ) } diff --git a/apps/laboratory/src/components/Theming/MixColorInput.tsx b/apps/laboratory/src/components/Theming/MixColorInput.tsx index 310062a295..c79dae9b08 100644 --- a/apps/laboratory/src/components/Theming/MixColorInput.tsx +++ b/apps/laboratory/src/components/Theming/MixColorInput.tsx @@ -1,18 +1,13 @@ -import { - Grid, - Heading, - Slider, - SliderFilledTrack, - SliderMark, - SliderThumb, - SliderTrack, - useRadioGroup -} from '@chakra-ui/react' +import { Grid, useRadioGroup } from '@chakra-ui/react' import { colors } from '../../utils/DataUtil' import RadioColor from './RadioColor' import { ThemeStore } from '../../utils/StoreUtil' import { useProxy } from 'valtio/utils' +import { Span } from '@/components/ui/typography' +import { Slider } from '@/components/ui/slider' +import { cn } from '@/lib/utils' +import { Row } from '@/components/ui/row' export default function MixColorInput() { const state = useProxy(ThemeStore.state) @@ -31,13 +26,10 @@ export default function MixColorInput() { return ( <> - - Mix Color - + Mix Color {colors.map(value => { const radio = getRadioProps({ value }) - return ( {value} @@ -45,31 +37,20 @@ export default function MixColorInput() { ) })} - { - ThemeStore.setMixColorStrength(val) - }} - > - - {state.mixColorStrength}% - - - - - - + + + { + ThemeStore.setMixColorStrength(value) + }} + /> + {state.mixColorStrength}% + ) } diff --git a/apps/laboratory/src/components/Theming/RadioColor.tsx b/apps/laboratory/src/components/Theming/RadioColor.tsx index d2d24853cd..18e172425c 100644 --- a/apps/laboratory/src/components/Theming/RadioColor.tsx +++ b/apps/laboratory/src/components/Theming/RadioColor.tsx @@ -1,4 +1,5 @@ -import { Box, useRadio, type RadioProps } from '@chakra-ui/react' +import { Column } from '@/components/ui/column' +import { useRadio, type RadioProps } from '@chakra-ui/react' export default function RadioColor(props: RadioProps) { const { getInputProps, getRadioProps } = useRadio(props) @@ -9,21 +10,13 @@ export default function RadioColor(props: RadioProps) { const backgroundColor = typeof props.children === 'string' ? props.children : 'transparent' return ( - + - - + > + ) } diff --git a/apps/laboratory/src/components/Wagmi/WagmiSendUSDCTest.tsx b/apps/laboratory/src/components/Wagmi/WagmiSendUSDCTest.tsx index 58385bcb12..7edb499313 100644 --- a/apps/laboratory/src/components/Wagmi/WagmiSendUSDCTest.tsx +++ b/apps/laboratory/src/components/Wagmi/WagmiSendUSDCTest.tsx @@ -1,7 +1,13 @@ -import { Button, useToast, Stack, Link, Text, Spacer, Input } from '@chakra-ui/react' +import { useToast } from '@chakra-ui/react' import { useAccount, useWriteContract } from 'wagmi' import { useCallback, useState } from 'react' import { optimism, sepolia } from 'wagmi/chains' +import { Button, buttonVariants } from '@/components/ui/button' +import { Span } from '@/components/ui/typography' +import { Column } from '@/components/ui/column' +import { Input } from '@/components/ui/input' +import Link from 'next/link' +import { cn } from '@/lib/utils' const minTokenAbi = [ { @@ -73,8 +79,7 @@ export function WagmiSendUSDCTest() { const allowedChains = [sepolia.id, optimism.id] as number[] return allowedChains.includes(Number(chain?.id)) && status === 'connected' ? ( - - + setAddress(e.target.value)} value={address} /> - - + + USDC Faucet - + ) : ( - + Switch to Sepolia or OP to test this feature - + ) } diff --git a/apps/laboratory/src/components/Wagmi/WagmiSignMessageTest.tsx b/apps/laboratory/src/components/Wagmi/WagmiSignMessageTest.tsx index 5f1e81350a..e98d53a887 100644 --- a/apps/laboratory/src/components/Wagmi/WagmiSignMessageTest.tsx +++ b/apps/laboratory/src/components/Wagmi/WagmiSignMessageTest.tsx @@ -1,6 +1,7 @@ -import { Button, useToast } from '@chakra-ui/react' import { useSignMessage, useAccount } from 'wagmi' import { ConstantsUtil } from '../../utils/ConstantsUtil' +import { Button } from '@/components/ui/button' +import { useToast } from '@chakra-ui/react' export function WagmiSignMessageTest() { const toast = useToast() @@ -28,7 +29,12 @@ export function WagmiSignMessageTest() { } return ( - ) diff --git a/apps/laboratory/src/components/Wagmi/WagmiSignTypedDataTest.tsx b/apps/laboratory/src/components/Wagmi/WagmiSignTypedDataTest.tsx index 627a1e33a0..12e6417df5 100644 --- a/apps/laboratory/src/components/Wagmi/WagmiSignTypedDataTest.tsx +++ b/apps/laboratory/src/components/Wagmi/WagmiSignTypedDataTest.tsx @@ -1,4 +1,5 @@ -import { Button, useToast } from '@chakra-ui/react' +import { Button } from '@/components/ui/button' +import { useToast } from '@chakra-ui/react' import { useAccount, useSignTypedData } from 'wagmi' // Example data @@ -62,7 +63,8 @@ export function WagmiSignTypedDataTest() { diff --git a/apps/laboratory/src/components/Wagmi/WagmiTests.tsx b/apps/laboratory/src/components/Wagmi/WagmiTests.tsx index 5c8ddfce85..47a3a127d3 100644 --- a/apps/laboratory/src/components/Wagmi/WagmiTests.tsx +++ b/apps/laboratory/src/components/Wagmi/WagmiTests.tsx @@ -1,53 +1,43 @@ import { WagmiTransactionTest } from './WagmiTransactionTest' import { WagmiSignMessageTest } from './WagmiSignMessageTest' import { WagmiSignTypedDataTest } from './WagmiSignTypedDataTest' -import { StackDivider, Card, CardHeader, Heading, CardBody, Box, Stack } from '@chakra-ui/react' import { WagmiWriteContractTest } from './WagmiWriteContractTest' import { WagmiSendUSDCTest } from './WagmiSendUSDCTest' +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' +import { Span } from '@/components/ui/typography' +import { Column } from '@/components/ui/column' export function WagmiTests() { return ( - - - Test Interactions + + + Test Interactions - - - } spacing="4"> - - - Sign Message - + + + + Sign Message - - - - - Sign Typed Data - + + + Sign Typed Data - + - - - Sign Transaction - + + Sign Transaction - - - - Contract Write - + + + Contract Write - - - - USDC Send - + + + USDC Send - - - + + + ) } diff --git a/apps/laboratory/src/components/Wagmi/WagmiTransactionTest.tsx b/apps/laboratory/src/components/Wagmi/WagmiTransactionTest.tsx index 9085b4e327..604b22c5f5 100644 --- a/apps/laboratory/src/components/Wagmi/WagmiTransactionTest.tsx +++ b/apps/laboratory/src/components/Wagmi/WagmiTransactionTest.tsx @@ -1,9 +1,15 @@ -import { Button, useToast, Stack, Link, Text, Spacer } from '@chakra-ui/react' +import { useToast } from '@chakra-ui/react' import { parseGwei, type Address } from 'viem' import { useEstimateGas, useSendTransaction, useAccount } from 'wagmi' import { vitalikEthAddress } from '../../utils/DataUtil' import { useCallback, useState } from 'react' import { optimism, optimismSepolia, sepolia } from 'wagmi/chains' +import { Span } from '@/components/ui/typography' +import { Row } from '@/components/ui/row' +import { cn } from '@/lib/utils' +import { Button, buttonVariants } from '@/components/ui/button' +import { Column } from '@/components/ui/column' +import Link from 'next/link' const TEST_TX = { to: vitalikEthAddress as Address, @@ -59,33 +65,36 @@ export function WagmiTransactionTest() { const allowedChains = [sepolia.id, optimism.id, optimismSepolia.id] as number[] return allowedChains.includes(Number(chain?.id)) && status === 'connected' ? ( - + - - - - - - - - - - + + + ) : ( - + Switch to Sepolia or OP to test this feature - + ) } diff --git a/apps/laboratory/src/components/Wagmi/WagmiWriteContractTest.tsx b/apps/laboratory/src/components/Wagmi/WagmiWriteContractTest.tsx index 54bd0af3b8..5fe1ee7aad 100644 --- a/apps/laboratory/src/components/Wagmi/WagmiWriteContractTest.tsx +++ b/apps/laboratory/src/components/Wagmi/WagmiWriteContractTest.tsx @@ -1,9 +1,15 @@ -import { Button, useToast, Stack, Link, Text, Spacer, Flex } from '@chakra-ui/react' +import { useToast } from '@chakra-ui/react' import { parseEther } from 'viem' import { useAccount, useSimulateContract, useWriteContract, useReadContract } from 'wagmi' import { useCallback, useEffect } from 'react' import { optimism, sepolia } from 'wagmi/chains' import { abi, address } from '../../utils/DonutContract' +import { Span } from '@/components/ui/typography' +import { Column } from '@/components/ui/column' +import { Button, buttonVariants } from '@/components/ui/button' +import { Row } from '@/components/ui/row' +import Link from 'next/link' +import { cn } from '@/lib/utils' export function WagmiWriteContractTest() { const toast = useToast() @@ -65,40 +71,44 @@ export function WagmiWriteContractTest() { const allowedChains = [sepolia.id, optimism.id] as number[] return allowedChains.includes(Number(chain?.id)) && status === 'connected' ? ( - - - {donutsQueryLoading || donutsQueryRefetching ? ( - Fetching donuts... - ) : ( - - Crypto donuts left: - {donutsOwned?.toString()} - - )} - + + + + {donutsQueryLoading || donutsQueryRefetching ? ( + Fetching donuts... + ) : ( + Crypto donuts left: {donutsOwned?.toString()} + )} + - - - + - - - - + + + ) : ( - + Switch to Sepolia or OP to test this feature - + ) } diff --git a/apps/laboratory/src/components/Web3ModalButtons.tsx b/apps/laboratory/src/components/Web3ModalButtons.tsx index 1b633ee34e..93552f1c8e 100644 --- a/apps/laboratory/src/components/Web3ModalButtons.tsx +++ b/apps/laboratory/src/components/Web3ModalButtons.tsx @@ -1,35 +1,30 @@ -import { Stack, Card, CardHeader, Heading, CardBody, Box, StackDivider } from '@chakra-ui/react' +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' +import { Column } from '@/components/ui/column' +import { Span } from '@/components/ui/typography' export function Web3ModalButtons() { return ( - - - Web3Modal Interactions + + + Web3Modal Interactions - - - } spacing="4"> - - - Connect / Account Button - + + + + Connect / Account Button - + - - - Network Button - + + Network Button - - - - Onramp Widget - + + + Onramp Widget - - - + + + ) } diff --git a/apps/laboratory/src/components/configuration-dialog.tsx b/apps/laboratory/src/components/configuration-dialog.tsx new file mode 100644 index 0000000000..3c35a4e611 --- /dev/null +++ b/apps/laboratory/src/components/configuration-dialog.tsx @@ -0,0 +1,47 @@ +import * as React from 'react' + +import { Column } from '@/components/ui/column' +import { Dialog, DialogContent } from '@/components/ui/dialog' +import { cn } from '@/lib/utils' +import { ThemeStore } from '@/utils/StoreUtil' +import MixColorInput from '@/components/Theming/MixColorInput' +import AccentColorInput from '@/components/Theming/AccentColorInput' +import BorderRadiusInput from '@/components/Theming/BorderRadiusInput' +import { GearIcon } from '@radix-ui/react-icons' +import { DialogTrigger } from '@radix-ui/react-dialog' +import { Button, buttonVariants } from '@/components/ui/button' +import { Span } from '@/components/ui/typography' +import { useTheme } from 'next-themes' + +export const ConfigurationDialog = () => { + const { theme } = useTheme() + + React.useEffect(() => { + if (ThemeStore.state.modal) { + ThemeStore.state.modal.setThemeMode(theme) + } + }, [theme]) + + return ( + + + + Configure + + + + + + + + + + + + ) +} diff --git a/apps/laboratory/src/components/icons/ethers.tsx b/apps/laboratory/src/components/icons/ethers.tsx new file mode 100644 index 0000000000..f7718f4692 --- /dev/null +++ b/apps/laboratory/src/components/icons/ethers.tsx @@ -0,0 +1,12 @@ +export default function EthersIcon(props: React.SVGProps) { + return ( + + + + ) +} diff --git a/apps/laboratory/src/components/icons/solana.tsx b/apps/laboratory/src/components/icons/solana.tsx new file mode 100644 index 0000000000..61b66274cf --- /dev/null +++ b/apps/laboratory/src/components/icons/solana.tsx @@ -0,0 +1,18 @@ +export default function SolanaIcon(props: React.SVGProps) { + return ( + + + + + + ) +} diff --git a/apps/laboratory/src/components/icons/wagmi.tsx b/apps/laboratory/src/components/icons/wagmi.tsx new file mode 100644 index 0000000000..d41c96bd83 --- /dev/null +++ b/apps/laboratory/src/components/icons/wagmi.tsx @@ -0,0 +1,19 @@ +export default function WagmiIcon(props: React.SVGProps) { + return ( + + + + + + + + + + + ) +} diff --git a/apps/laboratory/src/components/theme-provider.tsx b/apps/laboratory/src/components/theme-provider.tsx new file mode 100644 index 0000000000..06159b434e --- /dev/null +++ b/apps/laboratory/src/components/theme-provider.tsx @@ -0,0 +1,10 @@ +'use client' + +import * as React from 'react' + +import { ThemeProvider as NextThemesProvider } from 'next-themes' +import { type ThemeProviderProps } from 'next-themes/dist/types' + +export function ThemeProvider({ children, ...props }: ThemeProviderProps) { + return {children} +} diff --git a/apps/laboratory/src/components/theme-toggle.tsx b/apps/laboratory/src/components/theme-toggle.tsx new file mode 100644 index 0000000000..0b2f0c822b --- /dev/null +++ b/apps/laboratory/src/components/theme-toggle.tsx @@ -0,0 +1,40 @@ +'use client' + +import * as React from 'react' + +import { MoonIcon, SunIcon } from '@radix-ui/react-icons' +import { useTheme } from 'next-themes' + +import { Button } from '@/components/ui/button' +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger +} from '@/components/ui/dropdown-menu' +import { cn } from '@/lib/utils' + +type ModeToggleProps = { + className?: string +} + +export function ModeToggle({ className }: ModeToggleProps) { + const { setTheme } = useTheme() + + return ( + + + + + + setTheme('light')}>Light + setTheme('dark')}>Dark + setTheme('system')}>System + + + ) +} diff --git a/apps/laboratory/src/components/ui/button.tsx b/apps/laboratory/src/components/ui/button.tsx new file mode 100644 index 0000000000..09fa826e20 --- /dev/null +++ b/apps/laboratory/src/components/ui/button.tsx @@ -0,0 +1,49 @@ +import * as React from 'react' +import { Slot } from '@radix-ui/react-slot' +import { cva, type VariantProps } from 'class-variance-authority' + +import { cn } from '@/lib/utils' + +const buttonVariants = cva( + 'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50', + { + variants: { + variant: { + default: 'bg-primary text-primary-foreground hover:bg-primary/90', + destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90', + outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground', + secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80', + ghost: 'hover:bg-accent hover:text-accent-foreground', + link: 'text-primary underline-offset-4 hover:underline' + }, + size: { + default: 'h-10 px-4 py-2', + sm: 'h-9 rounded-md px-3', + lg: 'h-11 rounded-md px-8', + icon: 'h-10 w-10' + } + }, + defaultVariants: { + variant: 'default', + size: 'default' + } + } +) + +export interface ButtonProps + extends React.ButtonHTMLAttributes, + VariantProps { + asChild?: boolean +} + +const Button = React.forwardRef( + ({ className, variant, size, asChild = false, ...props }, ref) => { + const Comp = asChild ? Slot : 'button' + return ( + + ) + } +) +Button.displayName = 'Button' + +export { Button, buttonVariants } diff --git a/apps/laboratory/src/components/ui/card.tsx b/apps/laboratory/src/components/ui/card.tsx new file mode 100644 index 0000000000..a0147668d0 --- /dev/null +++ b/apps/laboratory/src/components/ui/card.tsx @@ -0,0 +1,56 @@ +import * as React from 'react' + +import { cn } from '@/lib/utils' + +const Card = React.forwardRef>( + ({ className, ...props }, ref) => ( +
+ ) +) +Card.displayName = 'Card' + +const CardHeader = React.forwardRef>( + ({ className, ...props }, ref) => ( +
+ ) +) +CardHeader.displayName = 'CardHeader' + +const CardTitle = React.forwardRef>( + ({ className, ...props }, ref) => ( +

+ ) +) +CardTitle.displayName = 'CardTitle' + +const CardDescription = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +

+)) +CardDescription.displayName = 'CardDescription' + +const CardContent = React.forwardRef>( + ({ className, ...props }, ref) => ( +

+ ) +) +CardContent.displayName = 'CardContent' + +const CardFooter = React.forwardRef>( + ({ className, ...props }, ref) => ( +
+ ) +) +CardFooter.displayName = 'CardFooter' + +export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } diff --git a/apps/laboratory/src/components/ui/column.tsx b/apps/laboratory/src/components/ui/column.tsx new file mode 100644 index 0000000000..02eef74274 --- /dev/null +++ b/apps/laboratory/src/components/ui/column.tsx @@ -0,0 +1,12 @@ +import * as React from 'react' + +import { cn } from '@/lib/utils' + +export interface ColumnProps extends React.HTMLAttributes {} + +const Column = React.forwardRef(({ className, ...props }, ref) => { + return
+}) +Column.displayName = 'Column' + +export { Column } diff --git a/apps/laboratory/src/components/ui/dialog.tsx b/apps/laboratory/src/components/ui/dialog.tsx new file mode 100644 index 0000000000..01ff19c7e4 --- /dev/null +++ b/apps/laboratory/src/components/ui/dialog.tsx @@ -0,0 +1,122 @@ +"use client" + +import * as React from "react" +import * as DialogPrimitive from "@radix-ui/react-dialog" +import { X } from "lucide-react" + +import { cn } from "@/lib/utils" + +const Dialog = DialogPrimitive.Root + +const DialogTrigger = DialogPrimitive.Trigger + +const DialogPortal = DialogPrimitive.Portal + +const DialogClose = DialogPrimitive.Close + +const DialogOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DialogOverlay.displayName = DialogPrimitive.Overlay.displayName + +const DialogContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + {children} + + + Close + + + +)) +DialogContent.displayName = DialogPrimitive.Content.displayName + +const DialogHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +DialogHeader.displayName = "DialogHeader" + +const DialogFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +DialogFooter.displayName = "DialogFooter" + +const DialogTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DialogTitle.displayName = DialogPrimitive.Title.displayName + +const DialogDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DialogDescription.displayName = DialogPrimitive.Description.displayName + +export { + Dialog, + DialogPortal, + DialogOverlay, + DialogClose, + DialogTrigger, + DialogContent, + DialogHeader, + DialogFooter, + DialogTitle, + DialogDescription, +} diff --git a/apps/laboratory/src/components/ui/dropdown-menu.tsx b/apps/laboratory/src/components/ui/dropdown-menu.tsx new file mode 100644 index 0000000000..f69a0d64ca --- /dev/null +++ b/apps/laboratory/src/components/ui/dropdown-menu.tsx @@ -0,0 +1,200 @@ +"use client" + +import * as React from "react" +import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu" +import { Check, ChevronRight, Circle } from "lucide-react" + +import { cn } from "@/lib/utils" + +const DropdownMenu = DropdownMenuPrimitive.Root + +const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger + +const DropdownMenuGroup = DropdownMenuPrimitive.Group + +const DropdownMenuPortal = DropdownMenuPrimitive.Portal + +const DropdownMenuSub = DropdownMenuPrimitive.Sub + +const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup + +const DropdownMenuSubTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean + } +>(({ className, inset, children, ...props }, ref) => ( + + {children} + + +)) +DropdownMenuSubTrigger.displayName = + DropdownMenuPrimitive.SubTrigger.displayName + +const DropdownMenuSubContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DropdownMenuSubContent.displayName = + DropdownMenuPrimitive.SubContent.displayName + +const DropdownMenuContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, sideOffset = 4, ...props }, ref) => ( + + + +)) +DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName + +const DropdownMenuItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean + } +>(({ className, inset, ...props }, ref) => ( + +)) +DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName + +const DropdownMenuCheckboxItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, checked, ...props }, ref) => ( + + + + + + + {children} + +)) +DropdownMenuCheckboxItem.displayName = + DropdownMenuPrimitive.CheckboxItem.displayName + +const DropdownMenuRadioItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + + + + {children} + +)) +DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName + +const DropdownMenuLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean + } +>(({ className, inset, ...props }, ref) => ( + +)) +DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName + +const DropdownMenuSeparator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName + +const DropdownMenuShortcut = ({ + className, + ...props +}: React.HTMLAttributes) => { + return ( + + ) +} +DropdownMenuShortcut.displayName = "DropdownMenuShortcut" + +export { + DropdownMenu, + DropdownMenuTrigger, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuCheckboxItem, + DropdownMenuRadioItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuGroup, + DropdownMenuPortal, + DropdownMenuSub, + DropdownMenuSubContent, + DropdownMenuSubTrigger, + DropdownMenuRadioGroup, +} diff --git a/apps/laboratory/src/components/ui/input.tsx b/apps/laboratory/src/components/ui/input.tsx new file mode 100644 index 0000000000..677d05fd6c --- /dev/null +++ b/apps/laboratory/src/components/ui/input.tsx @@ -0,0 +1,25 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +export interface InputProps + extends React.InputHTMLAttributes {} + +const Input = React.forwardRef( + ({ className, type, ...props }, ref) => { + return ( + + ) + } +) +Input.displayName = "Input" + +export { Input } diff --git a/apps/laboratory/src/components/ui/row.tsx b/apps/laboratory/src/components/ui/row.tsx new file mode 100644 index 0000000000..4d2fd29b08 --- /dev/null +++ b/apps/laboratory/src/components/ui/row.tsx @@ -0,0 +1,12 @@ +import * as React from 'react' + +import { cn } from '@/lib/utils' + +export interface RowProps extends React.HTMLAttributes {} + +const Row = React.forwardRef(({ className, ...props }, ref) => { + return
+}) +Row.displayName = 'Row' + +export { Row } diff --git a/apps/laboratory/src/components/ui/slider.tsx b/apps/laboratory/src/components/ui/slider.tsx new file mode 100644 index 0000000000..c31c2b3bc1 --- /dev/null +++ b/apps/laboratory/src/components/ui/slider.tsx @@ -0,0 +1,28 @@ +"use client" + +import * as React from "react" +import * as SliderPrimitive from "@radix-ui/react-slider" + +import { cn } from "@/lib/utils" + +const Slider = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + + + +)) +Slider.displayName = SliderPrimitive.Root.displayName + +export { Slider } diff --git a/apps/laboratory/src/components/ui/typography.tsx b/apps/laboratory/src/components/ui/typography.tsx new file mode 100644 index 0000000000..3a345f6fb6 --- /dev/null +++ b/apps/laboratory/src/components/ui/typography.tsx @@ -0,0 +1,150 @@ +import { ReactNode } from 'react' + +import { cn } from '@/lib/utils' + +type ElementProps = { + children?: ReactNode + className?: string +} + +declare type RFC = React.FC + +export const H1: RFC = ({ children, ...props }) => { + return ( +

+ {children} +

+ ) +} +export const H2: RFC = ({ children, ...props }) => { + return ( +

+ {children} +

+ ) +} +export const H3: RFC = ({ children, ...props }) => { + return ( +

+ {children} +

+ ) +} +export const H4: RFC = ({ children, ...props }) => { + return ( +

+ {children} +

+ ) +} + +export const P: RFC = ({ children, ...props }) => { + return ( +

+ {children} +

+ ) +} +export const Span: RFC = ({ children, ...props }) => { + return ( + + {children} + + ) +} + +export const Blockquote: RFC = ({ children, ...props }) => { + return ( +
+ {children} +
+ ) +} +export const List: RFC = ({ children, ...props }) => { + return ( +
    li]:mt-2', props.className)} {...props}> + {children} +
+ ) +} +export const ListItem: RFC = ({ children, ...props }) => { + return
  • {children}
  • +} +export const InlineCode: RFC = ({ children, ...props }) => { + return ( + + {children} + + ) +} +export const Lead: RFC = ({ children, ...props }) => { + return

    {children}

    +} + +type TableSubComponents = { + Head: RFC + HeadRow: RFC + HeadRowItem: RFC + Body: RFC + BodyRow: RFC + BodyRowItem: RFC +} + +export const Table: RFC & TableSubComponents = ({ children, ...props }) => { + return {children}
    +} +Table.Head = ({ children, ...props }) => { + return {children} +} +Table.HeadRow = ({ children, ...props }) => { + return {children} +} +Table.HeadRowItem = ({ children, ...props }) => { + return ( + + {children} + + ) +} +Table.Body = ({ children, ...props }) => { + return {children} +} +Table.BodyRow = ({ children, ...props }) => { + return {children} +} +Table.BodyRowItem = ({ children, ...props }) => { + return ( + + {children} + + ) +} diff --git a/apps/laboratory/src/layout/LayoutHeader.tsx b/apps/laboratory/src/layout/LayoutHeader.tsx index c26788c0d2..8cef83f5e6 100644 --- a/apps/laboratory/src/layout/LayoutHeader.tsx +++ b/apps/laboratory/src/layout/LayoutHeader.tsx @@ -1,46 +1,56 @@ -import { - Image, - Stack, - HStack, - Button, - Spacer, - Link as CLink, - useDisclosure -} from '@chakra-ui/react' import Link from 'next/link' -import { IoSettingsOutline } from 'react-icons/io5' -import { OptionsDrawer } from './OptionsDrawer' +import { Column } from '@/components/ui/column' +import Image from 'next/image' +import { Row } from '@/components/ui/row' +import { cn } from '@/lib/utils' +import { buttonVariants } from '@/components/ui/button' +import { GitHubLogoIcon, MixIcon, ReaderIcon } from '@radix-ui/react-icons' +import { ModeToggle } from '@/components/theme-toggle' +import { Span } from '@/components/ui/typography' +import { ConfigurationDialog } from '@/components/configuration-dialog' +import { useDisclosure } from '@chakra-ui/react' export function LayoutHeader() { const controls = useDisclosure() return ( - <> - - - - + + + web3modal logo + - + + + + + GitHub + + + + Components + + + + Docs + + - - - GitHub - - - Gallery - - - Docs - - + - - - - - + + + ) } diff --git a/apps/laboratory/src/layout/OptionsDrawer.tsx b/apps/laboratory/src/layout/OptionsDrawer.tsx deleted file mode 100644 index 86807bcd1a..0000000000 --- a/apps/laboratory/src/layout/OptionsDrawer.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import { - Drawer, - DrawerBody, - DrawerCloseButton, - DrawerContent, - DrawerHeader, - DrawerOverlay, - Flex, - Icon, - useColorMode, - useDisclosure -} from '@chakra-ui/react' -import { BsFillMoonFill, BsFillSunFill } from 'react-icons/bs' -import AccentColorInput from '../components/Theming/AccentColorInput' -import BorderRadiusInput from '../components/Theming/BorderRadiusInput' -import MixColorInput from '../components/Theming/MixColorInput' -import { useEffect } from 'react' -import { ThemeStore } from '../utils/StoreUtil' - -interface Props { - controls: ReturnType -} - -export function OptionsDrawer({ controls }: Props) { - const { colorMode, toggleColorMode } = useColorMode() - const { isOpen, onClose } = controls - - useEffect(() => { - if (ThemeStore.state.modal) { - ThemeStore.state.modal.setThemeMode(colorMode) - } - }, [colorMode]) - - return ( - - - - - Edit Theme - - {colorMode === 'light' ? ( - - - - ) : ( - - - - )} - - - - - - - - - - - - - - - - ) -} diff --git a/apps/laboratory/src/lib/utils.ts b/apps/laboratory/src/lib/utils.ts new file mode 100644 index 0000000000..d32b0fe652 --- /dev/null +++ b/apps/laboratory/src/lib/utils.ts @@ -0,0 +1,6 @@ +import { type ClassValue, clsx } from 'clsx' +import { twMerge } from 'tailwind-merge' + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)) +} diff --git a/apps/laboratory/src/pages/_app.tsx b/apps/laboratory/src/pages/_app.tsx index 8e8805c984..91fcce48a8 100644 --- a/apps/laboratory/src/pages/_app.tsx +++ b/apps/laboratory/src/pages/_app.tsx @@ -1,10 +1,18 @@ -import { ChakraProvider } from '@chakra-ui/react' import type { AppProps } from 'next/app' -import Layout from '../layout' import { bootstrapSentry } from '../utils/SentryUtil' import { SessionProvider } from 'next-auth/react' import type { Session } from 'next-auth' +import { Inter } from 'next/font/google' + +import '../../globals.css' +import { cn } from '@/lib/utils' +import { ThemeProvider } from '@/components/theme-provider' +import { ModeToggle } from '@/components/theme-toggle' +import { LayoutHeader } from '@/layout/LayoutHeader' + +const inter = Inter({ subsets: ['latin'] }) + bootstrapSentry() export default function App({ @@ -14,12 +22,15 @@ export default function App({ session: Session }>) { return ( - - - - - - - + + +
    +
    + + +
    +
    +
    +
    ) } diff --git a/apps/laboratory/src/pages/_document.tsx b/apps/laboratory/src/pages/_document.tsx index eff2ba5931..5e51ebfc7f 100644 --- a/apps/laboratory/src/pages/_document.tsx +++ b/apps/laboratory/src/pages/_document.tsx @@ -1,12 +1,11 @@ import { Html, Head, Main, NextScript } from 'next/document' -import { ColorModeScript } from '@chakra-ui/react' +import { cn } from '@/lib/utils' export default function Document() { return ( - - +
    diff --git a/apps/laboratory/src/pages/index.tsx b/apps/laboratory/src/pages/index.tsx index 8965d1e17d..47deb8d546 100644 --- a/apps/laboratory/src/pages/index.tsx +++ b/apps/laboratory/src/pages/index.tsx @@ -1,104 +1,95 @@ -import { - Heading, - Card, - CardHeader, - CardBody, - Stack, - StackDivider, - Box, - Text, - Button, - Link -} from '@chakra-ui/react' -import { IoArrowForward } from 'react-icons/io5' import { wagmiSdkOptions, ethersSdkOptions, solanaSdkOptions } from '../utils/DataUtil' +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' +import { Column } from '@/components/ui/column' +import { Row } from '@/components/ui/row' +import { P, Span } from '@/components/ui/typography' +import Link from 'next/link' +import { Button } from '@/components/ui/button' +import { ArrowRightIcon } from '@radix-ui/react-icons' +import WagmiIcon from '@/components/icons/wagmi' +import EthersIcon from '@/components/icons/ethers' +import SolanaIcon from '@/components/icons/solana' export default function HomePage() { return ( - <> - - - Wagmi + + + + + Wagmi - - - } spacing="4"> + + {wagmiSdkOptions.map(option => ( - - - - - {option.title} - - - {option.description} - - + + + + {option.title} +

    {option.description}

    +
    - + -
    -
    +
    + ))} -
    -
    +
    +
    - - - Ethers + + + + Ethers - - - } spacing="4"> + + {ethersSdkOptions.map(option => ( - - - - - {option.title} - - - {option.description} - - + + + + {option.title} +

    {option.description}

    +
    - + -
    -
    +
    + ))} -
    -
    + +
    - - - Solana + + + + Solana - - - } spacing="4"> + + {solanaSdkOptions.map(option => ( - - - - - {option.title} - - - {option.description} - - + + + + {option.title} +

    {option.description}

    +
    - + -
    -
    +
    + ))} -
    -
    + +
    - + ) } diff --git a/apps/laboratory/tailwind.config.js b/apps/laboratory/tailwind.config.js new file mode 100644 index 0000000000..262ca98d99 --- /dev/null +++ b/apps/laboratory/tailwind.config.js @@ -0,0 +1,72 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + darkMode: ['class'], + content: ['./src/**/*.{ts,tsx}'], + prefix: '', + theme: { + container: { + center: true, + padding: '2rem', + screens: { + '2xl': '1400px' + } + }, + extend: { + colors: { + border: 'hsl(var(--border))', + input: 'hsl(var(--input))', + ring: 'hsl(var(--ring))', + background: 'hsl(var(--background))', + foreground: 'hsl(var(--foreground))', + primary: { + DEFAULT: 'hsl(var(--primary))', + foreground: 'hsl(var(--primary-foreground))' + }, + secondary: { + DEFAULT: 'hsl(var(--secondary))', + foreground: 'hsl(var(--secondary-foreground))' + }, + destructive: { + DEFAULT: 'hsl(var(--destructive))', + foreground: 'hsl(var(--destructive-foreground))' + }, + muted: { + DEFAULT: 'hsl(var(--muted))', + foreground: 'hsl(var(--muted-foreground))' + }, + accent: { + DEFAULT: 'hsl(var(--accent))', + foreground: 'hsl(var(--accent-foreground))' + }, + popover: { + DEFAULT: 'hsl(var(--popover))', + foreground: 'hsl(var(--popover-foreground))' + }, + card: { + DEFAULT: 'hsl(var(--card))', + foreground: 'hsl(var(--card-foreground))' + } + }, + borderRadius: { + lg: 'var(--radius)', + md: 'calc(var(--radius) - 2px)', + sm: 'calc(var(--radius) - 4px)' + }, + keyframes: { + 'accordion-down': { + from: { height: '0' }, + to: { height: 'var(--radix-accordion-content-height)' } + }, + 'accordion-up': { + from: { height: 'var(--radix-accordion-content-height)' }, + to: { height: '0' } + } + }, + animation: { + 'accordion-down': 'accordion-down 0.2s ease-out', + 'accordion-up': 'accordion-up 0.2s ease-out' + } + } + }, + plugins: [require('tailwindcss-animate')] +} diff --git a/apps/laboratory/tsconfig.json b/apps/laboratory/tsconfig.json index 75754d6e68..479b60202d 100644 --- a/apps/laboratory/tsconfig.json +++ b/apps/laboratory/tsconfig.json @@ -2,7 +2,10 @@ "extends": "../../tsconfig.json", "compilerOptions": { "jsx": "preserve", - "noEmit": true + "noEmit": true, + "paths": { + "@/*": ["./src/*"] + } }, "include": ["next-env.d.ts", "src", "tests", "playwright.config.ts"] } From bd165ae36fa93426ccd94aabb9a20eaf2d023717 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Tue, 2 Apr 2024 13:50:56 +0300 Subject: [PATCH 44/96] chore: update lock file --- package-lock.json | 480 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 432 insertions(+), 48 deletions(-) diff --git a/package-lock.json b/package-lock.json index c79f5548a6..cfbc33c1e1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -161,6 +161,11 @@ "@chakra-ui/react": "2.8.2", "@emotion/react": "11.11.3", "@emotion/styled": "11.11.0", + "@radix-ui/react-dialog": "^1.0.5", + "@radix-ui/react-dropdown-menu": "^2.0.6", + "@radix-ui/react-icons": "^1.3.0", + "@radix-ui/react-slider": "^1.1.2", + "@radix-ui/react-slot": "^1.0.2", "@sentry/browser": "7.92.0", "@sentry/react": "7.92.0", "@solana/web3.js": "1.87.6", @@ -169,12 +174,16 @@ "@web3modal/siwe": "4.1.3-5f2ae345.1", "@web3modal/solana": "4.1.3-5f2ae345.1", "@web3modal/wagmi": "4.1.3-5f2ae345.1", + "class-variance-authority": "^0.7.0", "ethers": "6.9.0", "framer-motion": "10.17.9", + "lucide-react": "^0.364.0", "next": "14.0.4", "next-auth": "4.24.5", + "next-themes": "^0.3.0", "react-icons": "4.12.0", "siwe": "2.1.4", + "tailwindcss-animate": "^1.0.7", "valtio": "1.11.2", "viem": "2.9.3", "wagmi": "2.5.7" @@ -183,7 +192,49 @@ "@aws-sdk/client-cloudwatch": "3.509.0", "@mailsac/api": "1.0.5", "@playwright/test": "1.40.1", - "dotenv": "16.3.1" + "autoprefixer": "^10.4.19", + "clsx": "^2.1.0", + "dotenv": "16.3.1", + "postcss": "^8.4.38", + "tailwind-merge": "^2.2.2", + "tailwindcss": "^3.4.3" + } + }, + "apps/laboratory/node_modules/autoprefixer": { + "version": "10.4.19", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz", + "integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "browserslist": "^4.23.0", + "caniuse-lite": "^1.0.30001599", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" } }, "apps/laboratory/node_modules/framer-motion": { @@ -255,7 +306,7 @@ } } }, - "apps/laboratory/node_modules/postcss": { + "apps/laboratory/node_modules/next/node_modules/postcss": { "version": "8.4.31", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", @@ -282,6 +333,93 @@ "node": "^10 || ^12 || >=14" } }, + "apps/laboratory/node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "apps/laboratory/node_modules/postcss": { + "version": "8.4.38", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "apps/laboratory/node_modules/tailwind-merge": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.2.2.tgz", + "integrity": "sha512-tWANXsnmJzgw6mQ07nE3aCDkCK4QdT3ThPMCzawoYA2Pws7vSTCvz3Vrjg61jVUGfFZPJzxEP+NimbcW+EdaDw==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.24.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, + "apps/laboratory/node_modules/tailwindcss": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.3.tgz", + "integrity": "sha512-U7sxQk/n397Bmx4JHbJx/iSOOv5G+II3f1kpLpY2QeUv5DcPdcTsYLlusZfq1NthHS1c1cZoyFmmkex1rzke0A==", + "dev": true, + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.5.3", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.0", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.0", + "lilconfig": "^2.1.0", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.23", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.1", + "postcss-nested": "^6.0.1", + "postcss-selector-parser": "^6.0.11", + "resolve": "^1.22.2", + "sucrase": "^3.32.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, "apps/laboratory/node_modules/viem": { "version": "2.9.3", "resolved": "https://registry.npmjs.org/viem/-/viem-2.9.3.tgz", @@ -864,7 +1002,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", - "dev": true, "engines": { "node": ">=10" }, @@ -6853,7 +6990,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.0.tgz", "integrity": "sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==", - "dev": true, "dependencies": { "@floating-ui/utils": "^0.2.1" } @@ -6862,7 +6998,6 @@ "version": "1.6.3", "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.3.tgz", "integrity": "sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw==", - "dev": true, "dependencies": { "@floating-ui/core": "^1.0.0", "@floating-ui/utils": "^0.2.0" @@ -6872,7 +7007,6 @@ "version": "2.0.8", "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.8.tgz", "integrity": "sha512-HOdqOt3R3OGeTKidaLvJKcgg75S6tibQ3Tif4eyd91QnIJWr0NLvoXFpJA/j8HqkFSL68GDca9AuyWEHlhyClw==", - "dev": true, "dependencies": { "@floating-ui/dom": "^1.6.1" }, @@ -6884,8 +7018,7 @@ "node_modules/@floating-ui/utils": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz", - "integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==", - "dev": true + "integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==" }, "node_modules/@gitbeaker/core": { "version": "35.8.1", @@ -8595,7 +8728,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.0.1.tgz", "integrity": "sha512-T5gIdVO2mmPW3NNhjNgEP3cqMXjXL9UbO0BzWcXfvdBs+BohbQxvd/K5hSVKmn9/lbTdsQVKbUcP5WLCwvUbBg==", - "dev": true, "dependencies": { "@babel/runtime": "^7.13.10" } @@ -8612,7 +8744,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.0.3.tgz", "integrity": "sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==", - "dev": true, "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-primitive": "1.0.3" @@ -8636,7 +8767,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.3.tgz", "integrity": "sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==", - "dev": true, "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-compose-refs": "1.0.1", @@ -8832,7 +8962,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.0.1.tgz", "integrity": "sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==", - "dev": true, "dependencies": { "@babel/runtime": "^7.13.10" }, @@ -8874,6 +9003,35 @@ } } }, + "node_modules/@radix-ui/react-dropdown-menu": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.0.6.tgz", + "integrity": "sha512-i6TuFOoWmLWq+M/eCLGd/bQ2HfAX1RJgvrBQ6AQLmzfvsLdefxbWu8G9zczcPFfcSPehz9GcpF6K9QYreFV8hA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-menu": "2.0.6", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-controllable-state": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-focus-guards": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.1.tgz", @@ -8917,6 +9075,14 @@ } } }, + "node_modules/@radix-ui/react-icons": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-icons/-/react-icons-1.3.0.tgz", + "integrity": "sha512-jQxj/0LKgp+j9BiTXz3O3sgs26RNet2iLWmsPyRz2SIcR4q/4SbazXfnYwbAr+vLYKSfc7qxzyGQA1HLlYiuNw==", + "peerDependencies": { + "react": "^16.x || ^17.x || ^18.x" + } + }, "node_modules/@radix-ui/react-id": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.1.tgz", @@ -8935,6 +9101,177 @@ } } }, + "node_modules/@radix-ui/react-menu": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.0.6.tgz", + "integrity": "sha512-BVkFLS+bUC8HcImkRKPSiVumA1VPOOEC5WBMiT+QAVsPzW1FJzI9KnqgGxVDPBcql5xXrHkD3JOVoXWEXD8SYA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-collection": "1.0.3", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-direction": "1.0.1", + "@radix-ui/react-dismissable-layer": "1.0.5", + "@radix-ui/react-focus-guards": "1.0.1", + "@radix-ui/react-focus-scope": "1.0.4", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-popper": "1.1.3", + "@radix-ui/react-portal": "1.0.4", + "@radix-ui/react-presence": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-roving-focus": "1.0.4", + "@radix-ui/react-slot": "1.0.2", + "@radix-ui/react-use-callback-ref": "1.0.1", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "2.5.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.5.tgz", + "integrity": "sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-escape-keydown": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-focus-scope": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.4.tgz", + "integrity": "sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-popper": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.3.tgz", + "integrity": "sha512-cKpopj/5RHZWjrbF2846jBNacjQVwkP068DfmgrNJXpvVWrOvlAmE9xSiy5OqeE+Gi8D9fP+oDhUnPqNMY8/5w==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.0.3", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-layout-effect": "1.0.1", + "@radix-ui/react-use-rect": "1.0.1", + "@radix-ui/react-use-size": "1.0.1", + "@radix-ui/rect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-portal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.4.tgz", + "integrity": "sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menu/node_modules/react-remove-scroll": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz", + "integrity": "sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==", + "dependencies": { + "react-remove-scroll-bar": "^2.3.3", + "react-style-singleton": "^2.2.1", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.0", + "use-sidecar": "^1.1.2" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-popper": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.2.tgz", @@ -9043,7 +9380,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.0.4.tgz", "integrity": "sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==", - "dev": true, "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/primitive": "1.0.1", @@ -9164,6 +9500,39 @@ } } }, + "node_modules/@radix-ui/react-slider": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slider/-/react-slider-1.1.2.tgz", + "integrity": "sha512-NKs15MJylfzVsCagVSWKhGGLNR1W9qWs+HtgbmjjVUB3B9+lb3PYoXxVju3kOrpf0VKyVCtZp+iTwVoqpa1Chw==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/number": "1.0.1", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-collection": "1.0.3", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-direction": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-controllable-state": "1.0.1", + "@radix-ui/react-use-layout-effect": "1.0.1", + "@radix-ui/react-use-previous": "1.0.1", + "@radix-ui/react-use-size": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-slot": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz", @@ -9388,7 +9757,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.0.1.tgz", "integrity": "sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==", - "dev": true, "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/rect": "1.0.1" @@ -9449,7 +9817,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.0.1.tgz", "integrity": "sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==", - "dev": true, "dependencies": { "@babel/runtime": "^7.13.10" } @@ -15440,8 +15807,7 @@ "node_modules/any-promise": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "dev": true + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==" }, "node_modules/anymatch": { "version": "3.1.3", @@ -15474,8 +15840,7 @@ "node_modules/arg": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", - "dev": true + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" }, "node_modules/argparse": { "version": "2.0.1", @@ -16510,7 +16875,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", - "dev": true, "engines": { "node": ">= 6" } @@ -16719,6 +17083,25 @@ "consola": "^3.2.3" } }, + "node_modules/class-variance-authority": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.0.tgz", + "integrity": "sha512-jFI8IQw4hczaL4ALINxqLEXQbWcNjoSkloa4IaufXCJr6QawJyw7tuRysRsrE8w2p/4gGaxKIt/hX3qz/IbD1A==", + "dependencies": { + "clsx": "2.0.0" + }, + "funding": { + "url": "https://joebell.co.uk" + } + }, + "node_modules/class-variance-authority/node_modules/clsx": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz", + "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==", + "engines": { + "node": ">=6" + } + }, "node_modules/clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -17379,7 +17762,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true, "bin": { "cssesc": "bin/cssesc" }, @@ -18003,8 +18385,7 @@ "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", - "dev": true + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" }, "node_modules/diff-sequences": { "version": "29.6.3", @@ -18034,8 +18415,7 @@ "node_modules/dlv": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "dev": true + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" }, "node_modules/doctrine": { "version": "3.0.0", @@ -20441,7 +20821,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, "dependencies": { "is-glob": "^4.0.3" }, @@ -22700,7 +23079,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", - "dev": true, "engines": { "node": ">=10" } @@ -23045,6 +23423,14 @@ "yallist": "^3.0.2" } }, + "node_modules/lucide-react": { + "version": "0.364.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.364.0.tgz", + "integrity": "sha512-eHfdbJExWtTaZ0tBMGtI7PA/MbqV5wt+o4/yitDce17tadH/75Gq3Tq8jSteb3LhLr0eay/j5YUuN4yXjnI3aw==", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/magic-string": { "version": "0.30.8", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", @@ -24066,7 +24452,6 @@ "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "dev": true, "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", @@ -24189,6 +24574,15 @@ "node": ">= 0.6" } }, + "node_modules/next-themes": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.3.0.tgz", + "integrity": "sha512-/QHIrsYpd6Kfk7xakK4svpDI5mmXP0gfvCoJdGpZQ2TOrQZmsW0QxjaiLn8wbIKjtm4BTSqLoix4lxYYOnLJ/w==", + "peerDependencies": { + "react": "^16.8 || ^17 || ^18", + "react-dom": "^16.8 || ^17 || ^18" + } + }, "node_modules/next/node_modules/@next/env": { "version": "14.1.1", "resolved": "https://registry.npmjs.org/@next/env/-/env-14.1.1.tgz", @@ -25491,7 +25885,6 @@ "version": "8.4.35", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", - "devOptional": true, "funding": [ { "type": "opencollective", @@ -25519,7 +25912,6 @@ "version": "15.1.0", "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", - "dev": true, "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", @@ -25536,7 +25928,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", - "dev": true, "dependencies": { "camelcase-css": "^2.0.1" }, @@ -25555,7 +25946,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", - "dev": true, "funding": [ { "type": "opencollective", @@ -25590,7 +25980,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz", "integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==", - "dev": true, "engines": { "node": ">=14" }, @@ -25602,7 +25991,6 @@ "version": "2.4.1", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.1.tgz", "integrity": "sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==", - "dev": true, "bin": { "yaml": "bin.mjs" }, @@ -25614,7 +26002,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", - "dev": true, "dependencies": { "postcss-selector-parser": "^6.0.11" }, @@ -25633,7 +26020,6 @@ "version": "6.0.16", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz", "integrity": "sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==", - "dev": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -25645,8 +26031,7 @@ "node_modules/postcss-value-parser": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, "node_modules/preact": { "version": "10.20.1", @@ -26579,7 +26964,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", - "dev": true, "dependencies": { "pify": "^2.3.0" } @@ -26588,7 +26972,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -28239,7 +28622,6 @@ "version": "3.35.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", - "dev": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", @@ -28261,7 +28643,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true, "engines": { "node": ">= 6" } @@ -28390,7 +28771,6 @@ "version": "3.4.1", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.1.tgz", "integrity": "sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==", - "dev": true, "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", @@ -28423,11 +28803,18 @@ "node": ">=14.0.0" } }, + "node_modules/tailwindcss-animate": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz", + "integrity": "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==", + "peerDependencies": { + "tailwindcss": ">=3.0.0 || insiders" + } + }, "node_modules/tailwindcss/node_modules/object-hash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "dev": true, "engines": { "node": ">= 6" } @@ -28690,7 +29077,6 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "dev": true, "dependencies": { "any-promise": "^1.0.0" } @@ -28699,7 +29085,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "dev": true, "dependencies": { "thenify": ">= 3.1.0 < 4" }, @@ -28856,8 +29241,7 @@ "node_modules/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", - "dev": true + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==" }, "node_modules/ts-toolbelt": { "version": "9.6.0", From 00e0d0f5ef33ee411f199830072f32e83eca77d6 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Tue, 2 Apr 2024 13:51:09 +0300 Subject: [PATCH 45/96] chore: w3m color variables --- packages/ui/src/utils/ThemeUtil.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/ui/src/utils/ThemeUtil.ts b/packages/ui/src/utils/ThemeUtil.ts index 25611a4d6e..37fb0518a6 100644 --- a/packages/ui/src/utils/ThemeUtil.ts +++ b/packages/ui/src/utils/ThemeUtil.ts @@ -456,16 +456,16 @@ function createRootStyles(themeVariables?: ThemeVariables) { light: css` :root { --w3m-color-mix: ${unsafeCSS(themeVariables?.['--w3m-color-mix'] || '#fff')}; - --w3m-accent: ${unsafeCSS(themeVariables?.['--w3m-accent'] || '#47a1ff')}; + --w3m-accent: ${unsafeCSS(themeVariables?.['--w3m-accent'] || 'hsla(230, 100%, 67%, 1)')}; --w3m-default: #fff; - --wui-color-modal-bg-base: #191a1a; + --wui-color-modal-bg-base: #121313; --wui-color-blue-base-100: #47a1ff; --wui-color-accent-base-100: var(--w3m-accent); - --wui-color-accent-base-090: #59aaff; - --wui-color-accent-base-080: #6cb4ff; + --wui-color-accent-base-090: hsla(231, 76%, 61%, 1); + --wui-color-accent-base-080: hsla(230, 59%, 55%, 1); --wui-accent-glass-base-090: rgba(71, 161, 255, 0.9); --wui-accent-glass-base-080: rgba(71, 161, 255, 0.8); @@ -547,7 +547,7 @@ function createRootStyles(themeVariables?: ThemeVariables) { dark: css` :root { --w3m-color-mix: ${unsafeCSS(themeVariables?.['--w3m-color-mix'] || '#000')}; - --w3m-accent: ${unsafeCSS(themeVariables?.['--w3m-accent'] || '#3396ff')}; + --w3m-accent: ${unsafeCSS(themeVariables?.['--w3m-accent'] || 'hsla(231, 100%, 70%, 1)')}; --w3m-default: #000; --wui-color-modal-bg-base: #fff; @@ -555,8 +555,8 @@ function createRootStyles(themeVariables?: ThemeVariables) { --wui-color-blue-base-100: #3396ff; --wui-color-accent-base-100: var(--w3m-accent); - --wui-color-accent-base-090: #2d7dd2; - --wui-color-accent-base-080: #2978cc; + --wui-color-accent-base-090: hsla(231, 97%, 72%, 1); + --wui-color-accent-base-080: hsla(231, 92%, 74%, 1); --wui-accent-glass-base-090: rgba(51, 150, 255, 0.9); --wui-accent-glass-base-080: rgba(51, 150, 255, 0.8); From e5916f91507f269dd42006468ff7e16ad6b4be58 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Tue, 2 Apr 2024 15:27:08 +0300 Subject: [PATCH 46/96] chore: fix package versions and format files --- apps/laboratory/package.json | 28 ++++----- .../components/Theming/BorderRadiusInput.tsx | 2 +- .../src/components/Theming/MixColorInput.tsx | 2 +- .../src/components/configuration-dialog.tsx | 2 +- apps/laboratory/src/components/ui/dialog.tsx | 50 +++++----------- .../src/components/ui/dropdown-menu.tsx | 59 +++++++------------ apps/laboratory/src/components/ui/input.tsx | 11 ++-- apps/laboratory/src/components/ui/slider.tsx | 13 ++-- apps/laboratory/src/pages/_app.tsx | 1 - package-lock.json | 28 ++++----- 10 files changed, 79 insertions(+), 117 deletions(-) diff --git a/apps/laboratory/package.json b/apps/laboratory/package.json index 96207ad6fc..5985863228 100644 --- a/apps/laboratory/package.json +++ b/apps/laboratory/package.json @@ -25,11 +25,11 @@ "@chakra-ui/react": "2.8.2", "@emotion/react": "11.11.3", "@emotion/styled": "11.11.0", - "@radix-ui/react-dialog": "^1.0.5", - "@radix-ui/react-dropdown-menu": "^2.0.6", - "@radix-ui/react-icons": "^1.3.0", - "@radix-ui/react-slider": "^1.1.2", - "@radix-ui/react-slot": "^1.0.2", + "@radix-ui/react-dialog": "1.0.5", + "@radix-ui/react-dropdown-menu": "2.0.6", + "@radix-ui/react-icons": "1.3.0", + "@radix-ui/react-slider": "1.1.2", + "@radix-ui/react-slot": "1.0.2", "@sentry/browser": "7.92.0", "@sentry/react": "7.92.0", "@solana/web3.js": "1.87.6", @@ -38,16 +38,16 @@ "@web3modal/siwe": "4.1.3-5f2ae345.1", "@web3modal/solana": "4.1.3-5f2ae345.1", "@web3modal/wagmi": "4.1.3-5f2ae345.1", - "class-variance-authority": "^0.7.0", + "class-variance-authority": "0.7.0", "ethers": "6.9.0", "framer-motion": "10.17.9", - "lucide-react": "^0.364.0", + "lucide-react": "0.364.0", "next": "14.0.4", "next-auth": "4.24.5", - "next-themes": "^0.3.0", + "next-themes": "0.3.0", "react-icons": "4.12.0", "siwe": "2.1.4", - "tailwindcss-animate": "^1.0.7", + "tailwindcss-animate": "1.0.7", "valtio": "1.11.2", "viem": "2.9.3", "wagmi": "2.5.7" @@ -56,11 +56,11 @@ "@aws-sdk/client-cloudwatch": "3.509.0", "@mailsac/api": "1.0.5", "@playwright/test": "1.40.1", - "autoprefixer": "^10.4.19", - "clsx": "^2.1.0", + "autoprefixer": "10.4.19", + "clsx": "2.1.0", "dotenv": "16.3.1", - "postcss": "^8.4.38", - "tailwind-merge": "^2.2.2", - "tailwindcss": "^3.4.3" + "postcss": "8.4.38", + "tailwind-merge": "2.2.2", + "tailwindcss": "3.4.3" } } diff --git a/apps/laboratory/src/components/Theming/BorderRadiusInput.tsx b/apps/laboratory/src/components/Theming/BorderRadiusInput.tsx index a678f7c352..18f55d3ca2 100644 --- a/apps/laboratory/src/components/Theming/BorderRadiusInput.tsx +++ b/apps/laboratory/src/components/Theming/BorderRadiusInput.tsx @@ -20,7 +20,7 @@ export default function BorderRadiusInput() { step={1} className={cn('w-[60%]')} onValueChange={value => { - ThemeStore.setBorderRadius(`${value}px`) + ThemeStore.setBorderRadius(`${value[0] || 0}px`) }} /> {state.borderRadius} diff --git a/apps/laboratory/src/components/Theming/MixColorInput.tsx b/apps/laboratory/src/components/Theming/MixColorInput.tsx index c79dae9b08..a08e28a666 100644 --- a/apps/laboratory/src/components/Theming/MixColorInput.tsx +++ b/apps/laboratory/src/components/Theming/MixColorInput.tsx @@ -46,7 +46,7 @@ export default function MixColorInput() { step={1} className={cn('w-[60%]')} onValueChange={value => { - ThemeStore.setMixColorStrength(value) + ThemeStore.setMixColorStrength(value[0] || 0) }} /> {state.mixColorStrength}% diff --git a/apps/laboratory/src/components/configuration-dialog.tsx b/apps/laboratory/src/components/configuration-dialog.tsx index 3c35a4e611..1cdf847962 100644 --- a/apps/laboratory/src/components/configuration-dialog.tsx +++ b/apps/laboratory/src/components/configuration-dialog.tsx @@ -9,7 +9,7 @@ import AccentColorInput from '@/components/Theming/AccentColorInput' import BorderRadiusInput from '@/components/Theming/BorderRadiusInput' import { GearIcon } from '@radix-ui/react-icons' import { DialogTrigger } from '@radix-ui/react-dialog' -import { Button, buttonVariants } from '@/components/ui/button' +import { buttonVariants } from '@/components/ui/button' import { Span } from '@/components/ui/typography' import { useTheme } from 'next-themes' diff --git a/apps/laboratory/src/components/ui/dialog.tsx b/apps/laboratory/src/components/ui/dialog.tsx index 01ff19c7e4..aaaf449a31 100644 --- a/apps/laboratory/src/components/ui/dialog.tsx +++ b/apps/laboratory/src/components/ui/dialog.tsx @@ -1,10 +1,10 @@ -"use client" +'use client' -import * as React from "react" -import * as DialogPrimitive from "@radix-ui/react-dialog" -import { X } from "lucide-react" +import * as React from 'react' +import * as DialogPrimitive from '@radix-ui/react-dialog' +import { X } from 'lucide-react' -import { cn } from "@/lib/utils" +import { cn } from '@/lib/utils' const Dialog = DialogPrimitive.Root @@ -21,7 +21,7 @@ const DialogOverlay = React.forwardRef< ) => ( -
    +const DialogHeader = ({ className, ...props }: React.HTMLAttributes) => ( +
    ) -DialogHeader.displayName = "DialogHeader" +DialogHeader.displayName = 'DialogHeader' -const DialogFooter = ({ - className, - ...props -}: React.HTMLAttributes) => ( +const DialogFooter = ({ className, ...props }: React.HTMLAttributes) => (
    ) -DialogFooter.displayName = "DialogFooter" +DialogFooter.displayName = 'DialogFooter' const DialogTitle = React.forwardRef< React.ElementRef, @@ -87,10 +72,7 @@ const DialogTitle = React.forwardRef< >(({ className, ...props }, ref) => ( )) @@ -102,7 +84,7 @@ const DialogDescription = React.forwardRef< >(({ className, ...props }, ref) => ( )) @@ -118,5 +100,5 @@ export { DialogHeader, DialogFooter, DialogTitle, - DialogDescription, + DialogDescription } diff --git a/apps/laboratory/src/components/ui/dropdown-menu.tsx b/apps/laboratory/src/components/ui/dropdown-menu.tsx index f69a0d64ca..209b0294ce 100644 --- a/apps/laboratory/src/components/ui/dropdown-menu.tsx +++ b/apps/laboratory/src/components/ui/dropdown-menu.tsx @@ -1,10 +1,10 @@ -"use client" +'use client' -import * as React from "react" -import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu" -import { Check, ChevronRight, Circle } from "lucide-react" +import * as React from 'react' +import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu' +import { Check, ChevronRight, Circle } from 'lucide-react' -import { cn } from "@/lib/utils" +import { cn } from '@/lib/utils' const DropdownMenu = DropdownMenuPrimitive.Root @@ -27,8 +27,8 @@ const DropdownMenuSubTrigger = React.forwardRef< )) -DropdownMenuSubTrigger.displayName = - DropdownMenuPrimitive.SubTrigger.displayName +DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName const DropdownMenuSubContent = React.forwardRef< React.ElementRef, @@ -47,14 +46,13 @@ const DropdownMenuSubContent = React.forwardRef< )) -DropdownMenuSubContent.displayName = - DropdownMenuPrimitive.SubContent.displayName +DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName const DropdownMenuContent = React.forwardRef< React.ElementRef, @@ -65,7 +63,7 @@ const DropdownMenuContent = React.forwardRef< ref={ref} sideOffset={sideOffset} className={cn( - "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", + 'z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2', className )} {...props} @@ -83,8 +81,8 @@ const DropdownMenuItem = React.forwardRef< )) -DropdownMenuCheckboxItem.displayName = - DropdownMenuPrimitive.CheckboxItem.displayName +DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName const DropdownMenuRadioItem = React.forwardRef< React.ElementRef, @@ -123,7 +120,7 @@ const DropdownMenuRadioItem = React.forwardRef< (({ className, inset, ...props }, ref) => ( )) @@ -162,24 +155,16 @@ const DropdownMenuSeparator = React.forwardRef< >(({ className, ...props }, ref) => ( )) DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName -const DropdownMenuShortcut = ({ - className, - ...props -}: React.HTMLAttributes) => { - return ( - - ) +const DropdownMenuShortcut = ({ className, ...props }: React.HTMLAttributes) => { + return } -DropdownMenuShortcut.displayName = "DropdownMenuShortcut" +DropdownMenuShortcut.displayName = 'DropdownMenuShortcut' export { DropdownMenu, @@ -196,5 +181,5 @@ export { DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, - DropdownMenuRadioGroup, + DropdownMenuRadioGroup } diff --git a/apps/laboratory/src/components/ui/input.tsx b/apps/laboratory/src/components/ui/input.tsx index 677d05fd6c..f396e4bc2b 100644 --- a/apps/laboratory/src/components/ui/input.tsx +++ b/apps/laboratory/src/components/ui/input.tsx @@ -1,9 +1,8 @@ -import * as React from "react" +import * as React from 'react' -import { cn } from "@/lib/utils" +import { cn } from '@/lib/utils' -export interface InputProps - extends React.InputHTMLAttributes {} +export interface InputProps extends React.InputHTMLAttributes {} const Input = React.forwardRef( ({ className, type, ...props }, ref) => { @@ -11,7 +10,7 @@ const Input = React.forwardRef( ( ) } ) -Input.displayName = "Input" +Input.displayName = 'Input' export { Input } diff --git a/apps/laboratory/src/components/ui/slider.tsx b/apps/laboratory/src/components/ui/slider.tsx index c31c2b3bc1..4622e78859 100644 --- a/apps/laboratory/src/components/ui/slider.tsx +++ b/apps/laboratory/src/components/ui/slider.tsx @@ -1,9 +1,9 @@ -"use client" +'use client' -import * as React from "react" -import * as SliderPrimitive from "@radix-ui/react-slider" +import * as React from 'react' +import * as SliderPrimitive from '@radix-ui/react-slider' -import { cn } from "@/lib/utils" +import { cn } from '@/lib/utils' const Slider = React.forwardRef< React.ElementRef, @@ -11,10 +11,7 @@ const Slider = React.forwardRef< >(({ className, ...props }, ref) => ( diff --git a/apps/laboratory/src/pages/_app.tsx b/apps/laboratory/src/pages/_app.tsx index 91fcce48a8..0fe4414cff 100644 --- a/apps/laboratory/src/pages/_app.tsx +++ b/apps/laboratory/src/pages/_app.tsx @@ -8,7 +8,6 @@ import { Inter } from 'next/font/google' import '../../globals.css' import { cn } from '@/lib/utils' import { ThemeProvider } from '@/components/theme-provider' -import { ModeToggle } from '@/components/theme-toggle' import { LayoutHeader } from '@/layout/LayoutHeader' const inter = Inter({ subsets: ['latin'] }) diff --git a/package-lock.json b/package-lock.json index cfbc33c1e1..f1e24c704f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -161,11 +161,11 @@ "@chakra-ui/react": "2.8.2", "@emotion/react": "11.11.3", "@emotion/styled": "11.11.0", - "@radix-ui/react-dialog": "^1.0.5", - "@radix-ui/react-dropdown-menu": "^2.0.6", - "@radix-ui/react-icons": "^1.3.0", - "@radix-ui/react-slider": "^1.1.2", - "@radix-ui/react-slot": "^1.0.2", + "@radix-ui/react-dialog": "1.0.5", + "@radix-ui/react-dropdown-menu": "2.0.6", + "@radix-ui/react-icons": "1.3.0", + "@radix-ui/react-slider": "1.1.2", + "@radix-ui/react-slot": "1.0.2", "@sentry/browser": "7.92.0", "@sentry/react": "7.92.0", "@solana/web3.js": "1.87.6", @@ -174,16 +174,16 @@ "@web3modal/siwe": "4.1.3-5f2ae345.1", "@web3modal/solana": "4.1.3-5f2ae345.1", "@web3modal/wagmi": "4.1.3-5f2ae345.1", - "class-variance-authority": "^0.7.0", + "class-variance-authority": "0.7.0", "ethers": "6.9.0", "framer-motion": "10.17.9", - "lucide-react": "^0.364.0", + "lucide-react": "0.364.0", "next": "14.0.4", "next-auth": "4.24.5", - "next-themes": "^0.3.0", + "next-themes": "0.3.0", "react-icons": "4.12.0", "siwe": "2.1.4", - "tailwindcss-animate": "^1.0.7", + "tailwindcss-animate": "1.0.7", "valtio": "1.11.2", "viem": "2.9.3", "wagmi": "2.5.7" @@ -192,12 +192,12 @@ "@aws-sdk/client-cloudwatch": "3.509.0", "@mailsac/api": "1.0.5", "@playwright/test": "1.40.1", - "autoprefixer": "^10.4.19", - "clsx": "^2.1.0", + "autoprefixer": "10.4.19", + "clsx": "2.1.0", "dotenv": "16.3.1", - "postcss": "^8.4.38", - "tailwind-merge": "^2.2.2", - "tailwindcss": "^3.4.3" + "postcss": "8.4.38", + "tailwind-merge": "2.2.2", + "tailwindcss": "3.4.3" } }, "apps/laboratory/node_modules/autoprefixer": { From 63a6cf116e04071c97e95c83372a737650ab38b1 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Tue, 2 Apr 2024 16:17:13 +0300 Subject: [PATCH 47/96] chore: add sonner --- apps/laboratory/package.json | 2 + .../Ethers/EthersSignMessageTest.tsx | 17 +- .../Ethers/EthersSignTypedDataTest.tsx | 12 +- .../Ethers/EthersTransactionTest.tsx | 17 +- .../Ethers/EthersWriteContractTest.tsx | 12 +- .../Header.tsx} | 5 +- .../Solana/SolanaSendTransactionTest.tsx | 41 ++--- .../Solana/SolanaSignMessageTest.tsx | 24 +-- .../Solana/SolanaSignTransactionTest.tsx | 41 ++--- .../components/Theming/AccentColorInput.tsx | 1 + .../src/components/Theming/MixColorInput.tsx | 1 + .../components/Wagmi/WagmiSendUSDCTest.tsx | 17 +- .../components/Wagmi/WagmiSignMessageTest.tsx | 17 +- .../Wagmi/WagmiSignTypedDataTest.tsx | 12 +- .../components/Wagmi/WagmiTransactionTest.tsx | 20 +-- .../Wagmi/WagmiWriteContractTest.tsx | 26 +-- .../src/components/configuration-dialog.tsx | 2 +- apps/laboratory/src/components/ui/button.tsx | 1 + apps/laboratory/src/components/ui/column.tsx | 10 +- apps/laboratory/src/components/ui/dialog.tsx | 166 +++++++++++------- .../src/components/ui/dropdown-menu.tsx | 5 +- apps/laboratory/src/components/ui/input.tsx | 4 +- apps/laboratory/src/components/ui/row.tsx | 10 +- .../src/components/ui/typography.tsx | 72 ++------ apps/laboratory/src/layout/index.tsx | 16 -- apps/laboratory/src/pages/_app.tsx | 5 +- package-lock.json | 10 ++ 27 files changed, 252 insertions(+), 314 deletions(-) rename apps/laboratory/src/{layout/LayoutHeader.tsx => components/Header.tsx} (93%) delete mode 100644 apps/laboratory/src/layout/index.tsx diff --git a/apps/laboratory/package.json b/apps/laboratory/package.json index 5985863228..f0fbd32d5a 100644 --- a/apps/laboratory/package.json +++ b/apps/laboratory/package.json @@ -5,6 +5,7 @@ "scripts": { "dev:laboratory": "next dev", "build:laboratory": "next build", + "format": "prettier --write .", "lint": "eslint . --ext .js,.jsx,.ts,.tsx", "playwright:start": "npm run dev:laboratory", "playwright:install": "playwright install --with-deps", @@ -47,6 +48,7 @@ "next-themes": "0.3.0", "react-icons": "4.12.0", "siwe": "2.1.4", + "sonner": "^1.4.41", "tailwindcss-animate": "1.0.7", "valtio": "1.11.2", "viem": "2.9.3", diff --git a/apps/laboratory/src/components/Ethers/EthersSignMessageTest.tsx b/apps/laboratory/src/components/Ethers/EthersSignMessageTest.tsx index e02c8e41c2..635fb7dcf0 100644 --- a/apps/laboratory/src/components/Ethers/EthersSignMessageTest.tsx +++ b/apps/laboratory/src/components/Ethers/EthersSignMessageTest.tsx @@ -1,11 +1,10 @@ -import { useToast } from '@chakra-ui/react' +import { toast } from 'sonner' import { useWeb3ModalAccount, useWeb3ModalProvider } from '@web3modal/ethers/react' import { BrowserProvider, JsonRpcSigner } from 'ethers' import { ConstantsUtil } from '../../utils/ConstantsUtil' import { Button } from '@/components/ui/button' export function EthersSignMessageTest() { - const toast = useToast() const { address, chainId } = useWeb3ModalAccount() const { walletProvider } = useWeb3ModalProvider() @@ -17,18 +16,12 @@ export function EthersSignMessageTest() { const provider = new BrowserProvider(walletProvider, chainId) const signer = new JsonRpcSigner(provider, address) const signature = await signer?.signMessage('Hello Web3Modal Ethers') - toast({ - title: ConstantsUtil.SigningSucceededToastTitle, - description: signature, - status: 'success', - isClosable: true + toast.success(ConstantsUtil.SigningSucceededToastTitle, { + description: signature }) } catch { - toast({ - title: ConstantsUtil.SigningFailedToastTitle, - description: 'Failed to sign message', - status: 'error', - isClosable: true + toast.error(ConstantsUtil.SigningFailedToastTitle, { + description: 'Failed to sign message' }) } } diff --git a/apps/laboratory/src/components/Ethers/EthersSignTypedDataTest.tsx b/apps/laboratory/src/components/Ethers/EthersSignTypedDataTest.tsx index a087afb0b1..4750a63a10 100644 --- a/apps/laboratory/src/components/Ethers/EthersSignTypedDataTest.tsx +++ b/apps/laboratory/src/components/Ethers/EthersSignTypedDataTest.tsx @@ -1,5 +1,5 @@ import { Button } from '@/components/ui/button' -import { useToast } from '@chakra-ui/react' +import { toast } from 'sonner' import { useWeb3ModalAccount, useWeb3ModalProvider } from '@web3modal/ethers/react' import { BrowserProvider, JsonRpcSigner } from 'ethers' import type { TypedDataField } from 'ethers' @@ -29,7 +29,6 @@ const message = { } as const export function EthersSignTypedDataTest() { - const toast = useToast() const { address, chainId } = useWeb3ModalAccount() const { walletProvider } = useWeb3ModalProvider() @@ -49,13 +48,10 @@ export function EthersSignTypedDataTest() { const signature = await signer?.signTypedData(domain, types, message) - toast({ title: 'Succcess', description: signature, status: 'success', isClosable: true }) + toast.success('Succcess', { description: signature }) } catch { - toast({ - title: 'Error', - description: 'Failed to sign message', - status: 'error', - isClosable: true + toast.error('Error', { + description: 'Failed to sign message' }) } } diff --git a/apps/laboratory/src/components/Ethers/EthersTransactionTest.tsx b/apps/laboratory/src/components/Ethers/EthersTransactionTest.tsx index d1f8ca2e16..30d244d4e5 100644 --- a/apps/laboratory/src/components/Ethers/EthersTransactionTest.tsx +++ b/apps/laboratory/src/components/Ethers/EthersTransactionTest.tsx @@ -1,4 +1,4 @@ -import { useToast, Link } from '@chakra-ui/react' +import { toast } from 'sonner' import { useWeb3ModalAccount, useWeb3ModalProvider } from '@web3modal/ethers/react' import { BrowserProvider, JsonRpcSigner, ethers } from 'ethers' import { sepolia, optimism } from '../../utils/ChainsUtil' @@ -9,9 +9,9 @@ import { cn } from '@/lib/utils' import { Row } from '@/components/ui/row' import { Column } from '@/components/ui/column' import { Span } from '@/components/ui/typography' +import Link from 'next/link' export function EthersTransactionTest() { - const toast = useToast() const { address, chainId } = useWeb3ModalAccount() const { walletProvider } = useWeb3ModalProvider() const [loading, setLoading] = useState(false) @@ -28,13 +28,10 @@ export function EthersTransactionTest() { to: vitalikEthAddress, value: ethers.parseUnits('0.0001', 'gwei') }) - toast({ title: 'Succcess', description: tx.hash, status: 'success', isClosable: true }) + toast.success('Success', { description: tx.hash }) } catch { - toast({ - title: 'Error', - description: 'Failed to sign transaction', - status: 'error', - isClosable: true + toast.error('Error', { + description: 'Failed to sign transaction' }) } finally { setLoading(false) @@ -57,7 +54,7 @@ export function EthersTransactionTest() { Sepolia Faucet 1 @@ -65,7 +62,7 @@ export function EthersTransactionTest() { Sepolia Faucet 2 diff --git a/apps/laboratory/src/components/Ethers/EthersWriteContractTest.tsx b/apps/laboratory/src/components/Ethers/EthersWriteContractTest.tsx index 4ff384c49d..0b1d03189d 100644 --- a/apps/laboratory/src/components/Ethers/EthersWriteContractTest.tsx +++ b/apps/laboratory/src/components/Ethers/EthersWriteContractTest.tsx @@ -1,4 +1,4 @@ -import { useToast } from '@chakra-ui/react' +import { toast } from 'sonner' import { useWeb3ModalAccount, useWeb3ModalProvider } from '@web3modal/ethers/react' import { BrowserProvider, JsonRpcSigner, ethers } from 'ethers' import { optimism, sepolia } from '../../utils/ChainsUtil' @@ -13,7 +13,6 @@ import { cn } from '@/lib/utils' import { Span } from '@/components/ui/typography' export function EthersWriteContractTest() { - const toast = useToast() const { address, chainId } = useWeb3ModalAccount() const { walletProvider } = useWeb3ModalProvider() const [loading, setLoading] = useState(false) @@ -29,13 +28,10 @@ export function EthersWriteContractTest() { const contract = new ethers.Contract(donutAddress, abi, signer) // @ts-expect-error ethers types are correct const tx = await contract.purchase(1, { value: ethers.parseEther('0.0001') }) - toast({ title: 'Succcess', description: tx.hash, status: 'success', isClosable: true }) + toast.success('Success', { description: tx.hash }) } catch { - toast({ - title: 'Error', - description: 'Failed to sign transaction', - status: 'error', - isClosable: true + toast.error('Error', { + description: 'Failed to sign transaction' }) } finally { setLoading(false) diff --git a/apps/laboratory/src/layout/LayoutHeader.tsx b/apps/laboratory/src/components/Header.tsx similarity index 93% rename from apps/laboratory/src/layout/LayoutHeader.tsx rename to apps/laboratory/src/components/Header.tsx index 8cef83f5e6..d36b0ac185 100644 --- a/apps/laboratory/src/layout/LayoutHeader.tsx +++ b/apps/laboratory/src/components/Header.tsx @@ -8,11 +8,8 @@ import { GitHubLogoIcon, MixIcon, ReaderIcon } from '@radix-ui/react-icons' import { ModeToggle } from '@/components/theme-toggle' import { Span } from '@/components/ui/typography' import { ConfigurationDialog } from '@/components/configuration-dialog' -import { useDisclosure } from '@chakra-ui/react' export function LayoutHeader() { - const controls = useDisclosure() - return ( @@ -49,7 +46,7 @@ export function LayoutHeader() { - + ) diff --git a/apps/laboratory/src/components/Solana/SolanaSendTransactionTest.tsx b/apps/laboratory/src/components/Solana/SolanaSendTransactionTest.tsx index d0bff000d9..52676da4ab 100644 --- a/apps/laboratory/src/components/Solana/SolanaSendTransactionTest.tsx +++ b/apps/laboratory/src/components/Solana/SolanaSendTransactionTest.tsx @@ -1,6 +1,7 @@ import { useState } from 'react' -import { Button, useToast, Stack, Text, Spacer, Link } from '@chakra-ui/react' +import { Stack, Text, Spacer } from '@chakra-ui/react' import { useWeb3ModalAccount, useWeb3ModalProvider } from '@web3modal/solana/react' +import { toast } from 'sonner' import { PublicKey, Transaction, @@ -11,13 +12,15 @@ import { } from '@solana/web3.js' import { solana } from '../../utils/ChainsUtil' +import { Button, buttonVariants } from '@/components/ui/button' +import { cn } from '@/lib/utils' +import Link from 'next/link' const PHANTOM_TESTNET_ADDRESS = 'EmT8r4E8ZjoQgt8sXGbaWBRMKfUXsVT1wonoSnJZ4nBn' const recipientAddress = new PublicKey(PHANTOM_TESTNET_ADDRESS) const amountInLamports = 100000000 export function SolanaSendTransactionTest() { - const toast = useToast() const { address, chainId } = useWeb3ModalAccount() const { walletProvider, connection } = useWeb3ModalProvider() const [loading, setLoading] = useState(false) @@ -53,13 +56,10 @@ export function SolanaSendTransactionTest() { transaction.recentBlockhash = blockhash const signature = await walletProvider.sendTransaction(transaction, connection as Connection) - toast({ title: 'Succcess', description: signature, status: 'success', isClosable: true }) + toast.success('Success', { description: signature }) } catch (err) { - toast({ - title: 'Error', - description: (err as Error).message, - status: 'error', - isClosable: true + toast.error('Success', { + description: (err as Error).message }) } finally { setLoading(false) @@ -106,13 +106,10 @@ export function SolanaSendTransactionTest() { transactionV0, connection as Connection ) - toast({ title: 'Succcess', description: signature, status: 'success', isClosable: true }) + toast.success('Success', { description: signature }) } catch (err) { - toast({ - title: 'Error', - description: (err as Error).message, - status: 'error', - isClosable: true + toast.error('Error', { + description: (err as Error).message }) } finally { setLoading(false) @@ -138,25 +135,29 @@ export function SolanaSendTransactionTest() { + {supportV0Transactions ? ( ) : null} - - + + Solana Faucet ) diff --git a/apps/laboratory/src/components/Solana/SolanaSignMessageTest.tsx b/apps/laboratory/src/components/Solana/SolanaSignMessageTest.tsx index ec15c427c1..c2172de382 100644 --- a/apps/laboratory/src/components/Solana/SolanaSignMessageTest.tsx +++ b/apps/laboratory/src/components/Solana/SolanaSignMessageTest.tsx @@ -1,4 +1,4 @@ -import { useToast } from '@chakra-ui/react' +import { toast } from 'sonner' import { useWeb3ModalAccount, useWeb3ModalProvider } from '@web3modal/solana/react' @@ -6,7 +6,6 @@ import { ConstantsUtil } from '../../utils/ConstantsUtil' import { Button } from '@/components/ui/button' export function SolanaSignMessageTest() { - const toast = useToast() const { address } = useWeb3ModalAccount() const { walletProvider } = useWeb3ModalProvider() @@ -21,27 +20,18 @@ export function SolanaSignMessageTest() { // Backpack has specific signature format now if ((signature as { signature: Uint8Array }).signature) { - toast({ - title: ConstantsUtil.SigningSucceededToastTitle, - description: (signature as { signature: Uint8Array }).signature, - status: 'success', - isClosable: true + toast.success(ConstantsUtil.SigningSucceededToastTitle, { + description: (signature as { signature: Uint8Array }).signature }) return } - toast({ - title: ConstantsUtil.SigningSucceededToastTitle, - description: signature as Uint8Array, - status: 'success', - isClosable: true + toast.success(ConstantsUtil.SigningSucceededToastTitle, { + description: signature as Uint8Array }) } catch (err) { - toast({ - title: ConstantsUtil.SigningFailedToastTitle, - description: 'Failed to sign message', - status: 'error', - isClosable: true + toast.error(ConstantsUtil.SigningFailedToastTitle, { + description: 'Failed to sign message' }) } } diff --git a/apps/laboratory/src/components/Solana/SolanaSignTransactionTest.tsx b/apps/laboratory/src/components/Solana/SolanaSignTransactionTest.tsx index 49cd7504f3..94334b500f 100644 --- a/apps/laboratory/src/components/Solana/SolanaSignTransactionTest.tsx +++ b/apps/laboratory/src/components/Solana/SolanaSignTransactionTest.tsx @@ -1,5 +1,6 @@ import { useState } from 'react' -import { Button, useToast, Stack, Text, Spacer, Link } from '@chakra-ui/react' +import { Stack, Text, Spacer } from '@chakra-ui/react' +import { toast } from 'sonner' import { PublicKey, Transaction, @@ -11,13 +12,15 @@ import { import { useWeb3ModalAccount, useWeb3ModalProvider } from '@web3modal/solana/react' import { solana } from '../../utils/ChainsUtil' +import { Button, buttonVariants } from '@/components/ui/button' +import Link from 'next/link' +import { cn } from '@/lib/utils' const PHANTOM_DEVNET_ADDRESS = 'EmT8r4E8ZjoQgt8sXGbaWBRMKfUXsVT1wonoSnJZ4nBn' const recipientAddress = new PublicKey(PHANTOM_DEVNET_ADDRESS) const amountInLamports = 100000000 export function SolanaSignTransactionTest() { - const toast = useToast() const { address, chainId } = useWeb3ModalAccount() const { walletProvider, connection } = useWeb3ModalProvider() const [loading, setLoading] = useState(false) @@ -48,13 +51,10 @@ export function SolanaSignTransactionTest() { const tx = await walletProvider.signTransaction(transaction) const signature = tx.signatures[0]?.signature - toast({ title: 'Succcess', description: signature, status: 'success', isClosable: true }) + toast.success('Success', { description: signature }) } catch (err) { - toast({ - title: 'Error', - description: 'Failed to sign transaction', - status: 'error', - isClosable: true + toast.success('Error', { + description: 'Failed to sign transaction' }) } finally { setLoading(false) @@ -93,13 +93,10 @@ export function SolanaSignTransactionTest() { const tx = await walletProvider.signTransaction(transactionV0) const signature = tx.signatures[0]?.signature - toast({ title: 'Succcess', description: signature, status: 'success', isClosable: true }) + toast.success('Success', { description: signature }) } catch (err) { - toast({ - title: 'Error', - description: 'Failed to sign transaction', - status: 'error', - isClosable: true + toast.success('Error', { + description: 'Failed to sign transaction' }) } finally { setLoading(false) @@ -125,7 +122,8 @@ export function SolanaSignTransactionTest() { @@ -133,17 +131,20 @@ export function SolanaSignTransactionTest() { ) : null} - - + + Solana Faucet ) diff --git a/apps/laboratory/src/components/Theming/AccentColorInput.tsx b/apps/laboratory/src/components/Theming/AccentColorInput.tsx index ee3023b265..819982a5e2 100644 --- a/apps/laboratory/src/components/Theming/AccentColorInput.tsx +++ b/apps/laboratory/src/components/Theming/AccentColorInput.tsx @@ -26,6 +26,7 @@ export default function AccentColorInput() { {colors.map(value => { const radio = getRadioProps({ value }) + return ( {value} diff --git a/apps/laboratory/src/components/Theming/MixColorInput.tsx b/apps/laboratory/src/components/Theming/MixColorInput.tsx index a08e28a666..afecbfe473 100644 --- a/apps/laboratory/src/components/Theming/MixColorInput.tsx +++ b/apps/laboratory/src/components/Theming/MixColorInput.tsx @@ -30,6 +30,7 @@ export default function MixColorInput() { {colors.map(value => { const radio = getRadioProps({ value }) + return ( {value} diff --git a/apps/laboratory/src/components/Wagmi/WagmiSendUSDCTest.tsx b/apps/laboratory/src/components/Wagmi/WagmiSendUSDCTest.tsx index 7edb499313..fb3a6341c5 100644 --- a/apps/laboratory/src/components/Wagmi/WagmiSendUSDCTest.tsx +++ b/apps/laboratory/src/components/Wagmi/WagmiSendUSDCTest.tsx @@ -1,4 +1,4 @@ -import { useToast } from '@chakra-ui/react' +import { toast } from 'sonner' import { useAccount, useWriteContract } from 'wagmi' import { useCallback, useState } from 'react' import { optimism, sepolia } from 'wagmi/chains' @@ -41,26 +41,19 @@ export function WagmiSendUSDCTest() { const [address, setAddress] = useState('') const [amount, setAmount] = useState('') const { status, chain } = useAccount() - const toast = useToast() const { writeContract } = useWriteContract({ mutation: { onSuccess: hash => { setLoading(false) - toast({ - title: 'Transaction Success', - description: hash, - status: 'success', - isClosable: true + toast.success('Transaction Success', { + description: hash }) }, onError: () => { setLoading(false) - toast({ - title: 'Error', - description: 'Failed to send transaction', - status: 'error', - isClosable: true + toast.error('Error', { + description: 'Failed to send transaction' }) } } diff --git a/apps/laboratory/src/components/Wagmi/WagmiSignMessageTest.tsx b/apps/laboratory/src/components/Wagmi/WagmiSignMessageTest.tsx index e98d53a887..a4c0b66eec 100644 --- a/apps/laboratory/src/components/Wagmi/WagmiSignMessageTest.tsx +++ b/apps/laboratory/src/components/Wagmi/WagmiSignMessageTest.tsx @@ -1,10 +1,9 @@ import { useSignMessage, useAccount } from 'wagmi' import { ConstantsUtil } from '../../utils/ConstantsUtil' import { Button } from '@/components/ui/button' -import { useToast } from '@chakra-ui/react' +import { toast } from 'sonner' export function WagmiSignMessageTest() { - const toast = useToast() const { signMessageAsync } = useSignMessage() const { status } = useAccount() const isConnected = status === 'connected' @@ -12,18 +11,12 @@ export function WagmiSignMessageTest() { async function onSignMessage() { try { const signature = await signMessageAsync({ message: 'Hello Web3Modal!' }) - toast({ - title: ConstantsUtil.SigningSucceededToastTitle, - description: signature, - status: 'success', - isClosable: true + toast.success(ConstantsUtil.SigningSucceededToastTitle, { + description: signature }) } catch { - toast({ - title: ConstantsUtil.SigningFailedToastTitle, - description: 'Failed to sign message', - status: 'error', - isClosable: true + toast.error(ConstantsUtil.SigningFailedToastTitle, { + description: 'Failed to sign message' }) } } diff --git a/apps/laboratory/src/components/Wagmi/WagmiSignTypedDataTest.tsx b/apps/laboratory/src/components/Wagmi/WagmiSignTypedDataTest.tsx index 12e6417df5..73790ea2ba 100644 --- a/apps/laboratory/src/components/Wagmi/WagmiSignTypedDataTest.tsx +++ b/apps/laboratory/src/components/Wagmi/WagmiSignTypedDataTest.tsx @@ -1,5 +1,5 @@ import { Button } from '@/components/ui/button' -import { useToast } from '@chakra-ui/react' +import { toast } from 'sonner' import { useAccount, useSignTypedData } from 'wagmi' // Example data @@ -28,7 +28,6 @@ const message = { } as const export function WagmiSignTypedDataTest() { - const toast = useToast() const { chain, status } = useAccount() const domain = { name: 'Ether Mail', @@ -48,13 +47,10 @@ export function WagmiSignTypedDataTest() { primaryType: 'Mail', types }) - toast({ title: 'Success', description: signature, status: 'success', isClosable: true }) + toast.success('Success', { description: signature }) } catch { - toast({ - title: 'Error', - description: 'Failed to sign message', - status: 'error', - isClosable: true + toast.error('Error', { + description: 'Failed to sign message' }) } } diff --git a/apps/laboratory/src/components/Wagmi/WagmiTransactionTest.tsx b/apps/laboratory/src/components/Wagmi/WagmiTransactionTest.tsx index 604b22c5f5..54ff1008b3 100644 --- a/apps/laboratory/src/components/Wagmi/WagmiTransactionTest.tsx +++ b/apps/laboratory/src/components/Wagmi/WagmiTransactionTest.tsx @@ -1,4 +1,4 @@ -import { useToast } from '@chakra-ui/react' +import { toast } from 'sonner' import { parseGwei, type Address } from 'viem' import { useEstimateGas, useSendTransaction, useAccount } from 'wagmi' import { vitalikEthAddress } from '../../utils/DataUtil' @@ -17,29 +17,23 @@ const TEST_TX = { } export function WagmiTransactionTest() { - const toast = useToast() const { status, chain } = useAccount() const { data: gas, error: prepareError } = useEstimateGas(TEST_TX) const [isLoading, setLoading] = useState(false) const isConnected = status === 'connected' + const { sendTransaction } = useSendTransaction({ mutation: { onSuccess: hash => { setLoading(false) - toast({ - title: 'Transaction Success', - description: hash, - status: 'success', - isClosable: true + toast.success('Transaction Success', { + description: hash }) }, onError: () => { setLoading(false) - toast({ - title: 'Error', - description: 'Failed to send transaction', - status: 'error', - isClosable: true + toast.error('Error', { + description: 'Failed to send transaction' }) } } @@ -69,7 +63,7 @@ export function WagmiTransactionTest() { ) diff --git a/apps/laboratory/src/components/Ethers/EthersSignTypedDataTest.tsx b/apps/laboratory/src/components/Ethers/EthersSignTypedDataTest.tsx index 4750a63a10..8a6f30e818 100644 --- a/apps/laboratory/src/components/Ethers/EthersSignTypedDataTest.tsx +++ b/apps/laboratory/src/components/Ethers/EthersSignTypedDataTest.tsx @@ -57,7 +57,7 @@ export function EthersSignTypedDataTest() { } return ( - ) diff --git a/apps/laboratory/src/components/Ethers/EthersTransactionTest.tsx b/apps/laboratory/src/components/Ethers/EthersTransactionTest.tsx index 30d244d4e5..69d3853a71 100644 --- a/apps/laboratory/src/components/Ethers/EthersTransactionTest.tsx +++ b/apps/laboratory/src/components/Ethers/EthersTransactionTest.tsx @@ -46,7 +46,7 @@ export function EthersTransactionTest() { data-test-id="sign-transaction-button" onClick={onSendTransaction} disabled={loading} - variant={'secondary'} + variant="secondary" > Send Transaction to Vitalik diff --git a/apps/laboratory/src/components/Ethers/EthersWriteContractTest.tsx b/apps/laboratory/src/components/Ethers/EthersWriteContractTest.tsx index 0b1d03189d..b7ee32ceff 100644 --- a/apps/laboratory/src/components/Ethers/EthersWriteContractTest.tsx +++ b/apps/laboratory/src/components/Ethers/EthersWriteContractTest.tsx @@ -45,7 +45,7 @@ export function EthersWriteContractTest() { data-test-id="sign-transaction-button" onClick={onSendTransaction} disabled={loading} - variant={'secondary'} + variant="secondary" > Purchase crypto donut diff --git a/apps/laboratory/src/components/Solana/SolanaSendTransactionTest.tsx b/apps/laboratory/src/components/Solana/SolanaSendTransactionTest.tsx index 52676da4ab..225f5dc033 100644 --- a/apps/laboratory/src/components/Solana/SolanaSendTransactionTest.tsx +++ b/apps/laboratory/src/components/Solana/SolanaSendTransactionTest.tsx @@ -136,7 +136,7 @@ export function SolanaSendTransactionTest() { data-test-id="sign-transaction-button" onClick={onSendTransaction} disabled={loading} - variant={'secondary'} + variant="secondary" > Sign and Send Transaction diff --git a/apps/laboratory/src/components/Solana/SolanaSignMessageTest.tsx b/apps/laboratory/src/components/Solana/SolanaSignMessageTest.tsx index c2172de382..d07f5c5474 100644 --- a/apps/laboratory/src/components/Solana/SolanaSignMessageTest.tsx +++ b/apps/laboratory/src/components/Solana/SolanaSignMessageTest.tsx @@ -37,7 +37,7 @@ export function SolanaSignMessageTest() { } return ( - ) diff --git a/apps/laboratory/src/components/Solana/SolanaSignTransactionTest.tsx b/apps/laboratory/src/components/Solana/SolanaSignTransactionTest.tsx index 94334b500f..c03b936c0b 100644 --- a/apps/laboratory/src/components/Solana/SolanaSignTransactionTest.tsx +++ b/apps/laboratory/src/components/Solana/SolanaSignTransactionTest.tsx @@ -123,7 +123,7 @@ export function SolanaSignTransactionTest() { data-test-id="sign-transaction-button" onClick={onSignTransaction} disabled={loading} - variant={'secondary'} + variant="secondary" > Sign Transaction @@ -132,7 +132,7 @@ export function SolanaSignTransactionTest() { data-test-id="sign-transaction-button" onClick={onSignVersionedTransaction} disabled={loading} - variant={'secondary'} + variant="secondary" > Sign Versioned Transaction diff --git a/apps/laboratory/src/components/Wagmi/WagmiSendUSDCTest.tsx b/apps/laboratory/src/components/Wagmi/WagmiSendUSDCTest.tsx index fb3a6341c5..c1ed26bd6c 100644 --- a/apps/laboratory/src/components/Wagmi/WagmiSendUSDCTest.tsx +++ b/apps/laboratory/src/components/Wagmi/WagmiSendUSDCTest.tsx @@ -86,7 +86,7 @@ export function WagmiSendUSDCTest() { onClick={onSendTransaction} disabled={!writeContract} aria-disabled={isLoading} - variant={'default'} + variant="default" > Send USDC diff --git a/apps/laboratory/src/components/Wagmi/WagmiSignMessageTest.tsx b/apps/laboratory/src/components/Wagmi/WagmiSignMessageTest.tsx index a4c0b66eec..685476ce9e 100644 --- a/apps/laboratory/src/components/Wagmi/WagmiSignMessageTest.tsx +++ b/apps/laboratory/src/components/Wagmi/WagmiSignMessageTest.tsx @@ -26,7 +26,7 @@ export function WagmiSignMessageTest() { data-testid="sign-message-button" onClick={onSignMessage} disabled={!isConnected} - variant={'secondary'} + variant="secondary" > Sign Message diff --git a/apps/laboratory/src/components/Wagmi/WagmiSignTypedDataTest.tsx b/apps/laboratory/src/components/Wagmi/WagmiSignTypedDataTest.tsx index 73790ea2ba..bfdd788545 100644 --- a/apps/laboratory/src/components/Wagmi/WagmiSignTypedDataTest.tsx +++ b/apps/laboratory/src/components/Wagmi/WagmiSignTypedDataTest.tsx @@ -60,7 +60,7 @@ export function WagmiSignTypedDataTest() { data-testid="sign-typed-data-button" onClick={onSignTypedData} disabled={!isConnected} - variant={'secondary'} + variant="secondary" > Sign Typed Data diff --git a/apps/laboratory/src/components/Wagmi/WagmiTransactionTest.tsx b/apps/laboratory/src/components/Wagmi/WagmiTransactionTest.tsx index 3af147451f..fc0e97b33e 100644 --- a/apps/laboratory/src/components/Wagmi/WagmiTransactionTest.tsx +++ b/apps/laboratory/src/components/Wagmi/WagmiTransactionTest.tsx @@ -61,7 +61,7 @@ export function WagmiTransactionTest() { data-test-id="sign-transaction-button" onClick={onSendTransaction} disabled={isLoading || !isConnected} - variant={'secondary'} + variant="secondary" > Send Transaction to Vitalik diff --git a/apps/laboratory/src/components/Wagmi/WagmiWriteContractTest.tsx b/apps/laboratory/src/components/Wagmi/WagmiWriteContractTest.tsx index b933bd7ba4..118ec7ec09 100644 --- a/apps/laboratory/src/components/Wagmi/WagmiWriteContractTest.tsx +++ b/apps/laboratory/src/components/Wagmi/WagmiWriteContractTest.tsx @@ -67,7 +67,7 @@ export function WagmiWriteContractTest() { data-test-id="sign-transaction-button" onClick={onSendTransaction} disabled={isPending || !isConnected} - variant={'secondary'} + variant="secondary" > Purchase crypto donut diff --git a/apps/laboratory/src/pages/index.tsx b/apps/laboratory/src/pages/index.tsx index 47deb8d546..e7e84546ba 100644 --- a/apps/laboratory/src/pages/index.tsx +++ b/apps/laboratory/src/pages/index.tsx @@ -28,7 +28,7 @@ export default function HomePage() {

    {option.description}

    - @@ -54,7 +54,7 @@ export default function HomePage() {

    {option.description}

    - @@ -80,7 +80,7 @@ export default function HomePage() {

    {option.description}

    - From 0c00a32d18969a8cf55d5feb34949099dca4b401 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Tue, 2 Apr 2024 16:29:58 +0300 Subject: [PATCH 50/96] chore: remove loose dependency --- apps/laboratory/package.json | 2 +- package-lock.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/laboratory/package.json b/apps/laboratory/package.json index f0fbd32d5a..4fbc5a10f8 100644 --- a/apps/laboratory/package.json +++ b/apps/laboratory/package.json @@ -48,7 +48,7 @@ "next-themes": "0.3.0", "react-icons": "4.12.0", "siwe": "2.1.4", - "sonner": "^1.4.41", + "sonner": "1.4.41", "tailwindcss-animate": "1.0.7", "valtio": "1.11.2", "viem": "2.9.3", diff --git a/package-lock.json b/package-lock.json index 723fd34922..44bd4b147c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -183,7 +183,7 @@ "next-themes": "0.3.0", "react-icons": "4.12.0", "siwe": "2.1.4", - "sonner": "^1.4.41", + "sonner": "1.4.41", "tailwindcss-animate": "1.0.7", "valtio": "1.11.2", "viem": "2.9.3", From 56db6d223bc480b94662c7b4a636a97a77f3f35f Mon Sep 17 00:00:00 2001 From: enesozturk Date: Tue, 2 Apr 2024 16:39:43 +0300 Subject: [PATCH 51/96] chore: hydration and styling issues --- apps/laboratory/globals.css | 3 +++ apps/laboratory/src/components/Ethers/EthersTests.tsx | 7 ++----- apps/laboratory/src/components/Web3ModalButtons.tsx | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/laboratory/globals.css b/apps/laboratory/globals.css index 97f2fa1380..23a3a392f5 100644 --- a/apps/laboratory/globals.css +++ b/apps/laboratory/globals.css @@ -74,4 +74,7 @@ body { @apply bg-background text-foreground; } + w3m-onramp-widget { + width: 100%; + } } diff --git a/apps/laboratory/src/components/Ethers/EthersTests.tsx b/apps/laboratory/src/components/Ethers/EthersTests.tsx index 8a75d739d7..de29869d8a 100644 --- a/apps/laboratory/src/components/Ethers/EthersTests.tsx +++ b/apps/laboratory/src/components/Ethers/EthersTests.tsx @@ -1,4 +1,3 @@ -import { useWeb3ModalAccount } from '@web3modal/ethers/react' import { EthersSignMessageTest } from './EthersSignMessageTest' import { EthersSignTypedDataTest } from './EthersSignTypedDataTest' import { EthersTransactionTest } from './EthersTransactionTest' @@ -8,9 +7,7 @@ import { Column } from '@/components/ui/column' import { Span } from '@/components/ui/typography' export function EthersTests() { - const { isConnected } = useWeb3ModalAccount() - - return isConnected ? ( + return ( Test Interactions @@ -34,5 +31,5 @@ export function EthersTests() { - ) : null + ) } diff --git a/apps/laboratory/src/components/Web3ModalButtons.tsx b/apps/laboratory/src/components/Web3ModalButtons.tsx index 93552f1c8e..f75dce6d2a 100644 --- a/apps/laboratory/src/components/Web3ModalButtons.tsx +++ b/apps/laboratory/src/components/Web3ModalButtons.tsx @@ -21,7 +21,7 @@ export function Web3ModalButtons() { Onramp Widget - + From 6e7f04ef4248b8d91669f44b7b886bfbfb2de3fa Mon Sep 17 00:00:00 2001 From: enesozturk Date: Tue, 2 Apr 2024 16:42:50 +0300 Subject: [PATCH 52/96] chore: svg props --- apps/laboratory/src/components/icons/wagmi.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/laboratory/src/components/icons/wagmi.tsx b/apps/laboratory/src/components/icons/wagmi.tsx index d41c96bd83..f00cbe8d77 100644 --- a/apps/laboratory/src/components/icons/wagmi.tsx +++ b/apps/laboratory/src/components/icons/wagmi.tsx @@ -1,10 +1,10 @@ export default function WagmiIcon(props: React.SVGProps) { return ( - + From 5ac15e5e5e36e958653ea73a07b71dde53ae9553 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Tue, 2 Apr 2024 16:47:22 +0300 Subject: [PATCH 53/96] chore: remove classname from the lit elements --- apps/laboratory/src/components/Web3ModalButtons.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/laboratory/src/components/Web3ModalButtons.tsx b/apps/laboratory/src/components/Web3ModalButtons.tsx index f75dce6d2a..93552f1c8e 100644 --- a/apps/laboratory/src/components/Web3ModalButtons.tsx +++ b/apps/laboratory/src/components/Web3ModalButtons.tsx @@ -21,7 +21,7 @@ export function Web3ModalButtons() { Onramp Widget - + From b2f4eb34de65d44d87d70650da67755d30001253 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Tue, 2 Apr 2024 16:58:56 +0300 Subject: [PATCH 54/96] chore: remove lucid, refactor class names --- apps/laboratory/package.json | 1 - .../src/components/configuration-dialog.tsx | 6 +++++- apps/laboratory/src/components/ui/dialog.tsx | 4 ++-- .../src/components/ui/dropdown-menu.tsx | 20 +++++++++++++------ package-lock.json | 9 --------- 5 files changed, 21 insertions(+), 19 deletions(-) diff --git a/apps/laboratory/package.json b/apps/laboratory/package.json index 4fbc5a10f8..ecea5866b7 100644 --- a/apps/laboratory/package.json +++ b/apps/laboratory/package.json @@ -42,7 +42,6 @@ "class-variance-authority": "0.7.0", "ethers": "6.9.0", "framer-motion": "10.17.9", - "lucide-react": "0.364.0", "next": "14.0.4", "next-auth": "4.24.5", "next-themes": "0.3.0", diff --git a/apps/laboratory/src/components/configuration-dialog.tsx b/apps/laboratory/src/components/configuration-dialog.tsx index 27a8db7162..d2524d9c81 100644 --- a/apps/laboratory/src/components/configuration-dialog.tsx +++ b/apps/laboratory/src/components/configuration-dialog.tsx @@ -30,7 +30,11 @@ export function ConfigurationDialog() { diff --git a/apps/laboratory/src/components/ui/dialog.tsx b/apps/laboratory/src/components/ui/dialog.tsx index 101de423d3..5f2a9bebe9 100644 --- a/apps/laboratory/src/components/ui/dialog.tsx +++ b/apps/laboratory/src/components/ui/dialog.tsx @@ -2,9 +2,9 @@ import * as React from 'react' import * as DialogPrimitive from '@radix-ui/react-dialog' -import { X } from 'lucide-react' import { cn } from '@/lib/utils' +import { Cross1Icon } from '@radix-ui/react-icons' const Dialog = DialogPrimitive.Root @@ -62,7 +62,7 @@ function DialogContent( > {children} - + Close diff --git a/apps/laboratory/src/components/ui/dropdown-menu.tsx b/apps/laboratory/src/components/ui/dropdown-menu.tsx index d6a98c9229..6f94d003ef 100644 --- a/apps/laboratory/src/components/ui/dropdown-menu.tsx +++ b/apps/laboratory/src/components/ui/dropdown-menu.tsx @@ -2,9 +2,9 @@ import * as React from 'react' import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu' -import { Check, ChevronRight, Circle } from 'lucide-react' import { cn } from '@/lib/utils' +import { CheckIcon, ChevronRightIcon, CircleIcon } from '@radix-ui/react-icons' const DropdownMenu = DropdownMenuPrimitive.Root @@ -34,7 +34,7 @@ const DropdownMenuSubTrigger = React.forwardRef< {...props} > {children} - + )) DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName @@ -46,7 +46,11 @@ const DropdownMenuSubContent = React.forwardRef< - + {children} @@ -127,7 +135,7 @@ const DropdownMenuRadioItem = React.forwardRef< > - + {children} diff --git a/package-lock.json b/package-lock.json index 44bd4b147c..210faf25e2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -177,7 +177,6 @@ "class-variance-authority": "0.7.0", "ethers": "6.9.0", "framer-motion": "10.17.9", - "lucide-react": "0.364.0", "next": "14.0.4", "next-auth": "4.24.5", "next-themes": "0.3.0", @@ -23433,14 +23432,6 @@ "yallist": "^3.0.2" } }, - "node_modules/lucide-react": { - "version": "0.364.0", - "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.364.0.tgz", - "integrity": "sha512-eHfdbJExWtTaZ0tBMGtI7PA/MbqV5wt+o4/yitDce17tadH/75Gq3Tq8jSteb3LhLr0eay/j5YUuN4yXjnI3aw==", - "peerDependencies": { - "react": "^16.5.1 || ^17.0.0 || ^18.0.0" - } - }, "node_modules/magic-string": { "version": "0.30.8", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", From 13d8dacd9aaf2262b471b33b235d487ed74befcb Mon Sep 17 00:00:00 2001 From: enesozturk Date: Tue, 2 Apr 2024 17:09:39 +0300 Subject: [PATCH 55/96] fix: dialog forwarded comps --- apps/laboratory/src/components/ui/dialog.tsx | 140 +++++++------------ 1 file changed, 52 insertions(+), 88 deletions(-) diff --git a/apps/laboratory/src/components/ui/dialog.tsx b/apps/laboratory/src/components/ui/dialog.tsx index 5f2a9bebe9..b5d8f76f73 100644 --- a/apps/laboratory/src/components/ui/dialog.tsx +++ b/apps/laboratory/src/components/ui/dialog.tsx @@ -14,61 +14,44 @@ const DialogPortal = DialogPrimitive.Portal const DialogClose = DialogPrimitive.Close -type DialogOverlayProps = React.ComponentPropsWithoutRef & { - className?: string -} +const DialogOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) -function DialogOverlay( - props: DialogOverlayProps, - ref: React.Ref> -) { - const { className, ...restProps } = props +DialogOverlay.displayName = DialogPrimitive.Overlay.displayName - return ( - , + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + - ) -} - -DialogOverlay.displayName = DialogPrimitive.Overlay.displayName - -type DialogContentProps = React.ComponentPropsWithoutRef & { - className?: string - children: React.ReactNode -} - -function DialogContent( - props: DialogContentProps, - ref: React.Ref> -) { - const { className, children } = props - - return ( - - - - {children} - - - Close - - - - ) -} + {...props} + > + {children} + + + Close + + + +)) DialogContent.displayName = DialogPrimitive.Content.displayName function DialogHeader({ className, ...props }: React.HTMLAttributes) { @@ -91,47 +74,28 @@ function DialogFooter({ className, ...props }: React.HTMLAttributes & { - className?: string -} - -function DialogTitle( - props: DialogTitleProps, - ref: React.Ref> -) { - const { className, ...restProps } = props - - return ( - - ) -} +const DialogTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) DialogTitle.displayName = DialogPrimitive.Title.displayName -type DialogDescriptionProps = React.ComponentPropsWithoutRef & { - className?: string -} - -// Define the component using a function definition -function DialogDescription( - props: DialogDescriptionProps, - ref: React.Ref> -) { - // Destructure className from props and collect the rest into a variable - const { className, ...restProps } = props - - // Return the JSX for the component - return ( - - ) -} +const DialogDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) DialogDescription.displayName = DialogPrimitive.Description.displayName export { From 1888fc73bd0f0e45e2de848a4f89aba9a8a07cf6 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Tue, 2 Apr 2024 17:15:21 +0300 Subject: [PATCH 56/96] chore: update comp locations, refactor class names --- apps/laboratory/src/components/Web3ModalButtons.tsx | 1 - .../{Theming => configuration-dialog}/AccentColorInput.tsx | 0 .../BorderRadiusInput.tsx | 1 - .../{Theming => configuration-dialog}/MixColorInput.tsx | 0 .../{Theming => configuration-dialog}/RadioColor.tsx | 0 .../index.tsx} | 7 ++++--- apps/laboratory/src/components/ui/dialog.tsx | 4 +++- 7 files changed, 7 insertions(+), 6 deletions(-) rename apps/laboratory/src/components/{Theming => configuration-dialog}/AccentColorInput.tsx (100%) rename apps/laboratory/src/components/{Theming => configuration-dialog}/BorderRadiusInput.tsx (99%) rename apps/laboratory/src/components/{Theming => configuration-dialog}/MixColorInput.tsx (100%) rename apps/laboratory/src/components/{Theming => configuration-dialog}/RadioColor.tsx (100%) rename apps/laboratory/src/components/{configuration-dialog.tsx => configuration-dialog/index.tsx} (89%) diff --git a/apps/laboratory/src/components/Web3ModalButtons.tsx b/apps/laboratory/src/components/Web3ModalButtons.tsx index 93552f1c8e..098c8d8e89 100644 --- a/apps/laboratory/src/components/Web3ModalButtons.tsx +++ b/apps/laboratory/src/components/Web3ModalButtons.tsx @@ -14,7 +14,6 @@ export function Web3ModalButtons() { Connect / Account Button - Network Button diff --git a/apps/laboratory/src/components/Theming/AccentColorInput.tsx b/apps/laboratory/src/components/configuration-dialog/AccentColorInput.tsx similarity index 100% rename from apps/laboratory/src/components/Theming/AccentColorInput.tsx rename to apps/laboratory/src/components/configuration-dialog/AccentColorInput.tsx diff --git a/apps/laboratory/src/components/Theming/BorderRadiusInput.tsx b/apps/laboratory/src/components/configuration-dialog/BorderRadiusInput.tsx similarity index 99% rename from apps/laboratory/src/components/Theming/BorderRadiusInput.tsx rename to apps/laboratory/src/components/configuration-dialog/BorderRadiusInput.tsx index 18f55d3ca2..9aa0fc88d2 100644 --- a/apps/laboratory/src/components/Theming/BorderRadiusInput.tsx +++ b/apps/laboratory/src/components/configuration-dialog/BorderRadiusInput.tsx @@ -11,7 +11,6 @@ export default function BorderRadiusInput() { return ( <> Border Radius - Date: Tue, 2 Apr 2024 20:20:54 +0300 Subject: [PATCH 57/96] chore: list more polygon tokens, add token balance watcher --- package-lock.json | 9 +-- .../core/src/controllers/ConvertController.ts | 2 +- packages/core/src/utils/ConstantsUtil.ts | 62 ++++++++++++++++++- .../w3m-account-tokens-widget/index.ts | 12 ++-- .../w3m-approve-transaction-view/index.ts | 2 +- 5 files changed, 72 insertions(+), 15 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8732ff0cfe..23d193b8a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27433,7 +27433,7 @@ "version": "3.29.4", "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", - "dev": true, + "devOptional": true, "bin": { "rollup": "dist/bin/rollup" }, @@ -27474,7 +27474,6 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "extraneous": true, "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", @@ -27488,7 +27487,6 @@ "version": "0.7.4", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "extraneous": true, "engines": { "node": ">= 8" } @@ -27497,7 +27495,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "extraneous": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -27514,7 +27511,6 @@ "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "extraneous": true, "engines": { "node": ">=10" } @@ -27523,7 +27519,6 @@ "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "extraneous": true, "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -27541,7 +27536,6 @@ "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "extraneous": true, "engines": { "node": ">=12" } @@ -32114,7 +32108,6 @@ "version": "4.1.3-5f2ae345.1", "license": "Apache-2.0", "dependencies": { - "bignumber.js": "^9.1.2", "lit": "3.1.0", "qrcode": "1.5.3" }, diff --git a/packages/core/src/controllers/ConvertController.ts b/packages/core/src/controllers/ConvertController.ts index 1affe7725f..4475add17f 100644 --- a/packages/core/src/controllers/ConvertController.ts +++ b/packages/core/src/controllers/ConvertController.ts @@ -291,7 +291,7 @@ export const ConvertController = { }, {}) state.suggestedTokens = Object.entries(res.tokens).reduce>( (limitedTokens, [tokenAddress, tokenInfo]) => { - if (ConstantsUtil.POPULAR_TOKENS.includes(tokenInfo.symbol)) { + if (ConstantsUtil.SUGGESTED_TOKENS.includes(tokenInfo.symbol)) { limitedTokens[tokenAddress] = tokenInfo } diff --git a/packages/core/src/utils/ConstantsUtil.ts b/packages/core/src/utils/ConstantsUtil.ts index 18af378e31..6033300656 100644 --- a/packages/core/src/utils/ConstantsUtil.ts +++ b/packages/core/src/utils/ConstantsUtil.ts @@ -63,7 +63,7 @@ export const ConstantsUtil = { WC_COINBASE_ONRAMP_APP_ID: 'bf18c88d-495a-463b-b249-0b9d3656cf5e', - POPULAR_TOKENS: [ + SUGGESTED_TOKENS: [ 'ETH', 'UNI', '1INCH', @@ -93,6 +93,66 @@ export const ConstantsUtil = { 'OP' ], + POPULAR_TOKENS: [ + 'ETH', + 'UNI', + '1INCH', + 'AAVE', + 'SOL', + 'ADA', + 'AVAX', + 'DOT', + 'LINK', + 'NITRO', + 'GAIA', + 'MILK', + 'TRX', + 'NEAR', + 'GNO', + 'WBTC', + 'DAI', + 'WETH', + 'USDC', + 'USDT', + 'ARB', + 'BAL', + 'BICO', + 'CRV', + 'ENS', + 'MATIC', + 'OP', + // Some Polygon tokens + 'DAI', + 'CHAMP', + 'WOLF', + 'SALE', + 'BAL', + 'BUSD', + 'MUST', + 'BTCpx', + 'ROUTE', + 'HEX', + 'WELT', + 'amDAI', + 'VSQ', + 'VISION', + 'AURUM', + 'pSP', + 'SNX', + 'VC', + 'LINK', + 'CHP', + 'amUSDT', + 'SPHERE', + 'FOX', + 'GIDDY', + 'GFC', + 'OMEN', + 'OX_OLD', + 'DE', + 'WNT' + ], + NATIVE_TOKEN_ADDRESS: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee' } diff --git a/packages/scaffold/src/partials/w3m-account-tokens-widget/index.ts b/packages/scaffold/src/partials/w3m-account-tokens-widget/index.ts index 808f7077ad..7263a53a50 100644 --- a/packages/scaffold/src/partials/w3m-account-tokens-widget/index.ts +++ b/packages/scaffold/src/partials/w3m-account-tokens-widget/index.ts @@ -9,6 +9,8 @@ export class W3mAccountTokensWidget extends LitElement { public static override styles = styles // -- Members ------------------------------------------- // + @state() private watchTokenBalance?: NodeJS.Timeout + private unsubscribe: (() => void)[] = [] // -- State & Properties -------------------------------- // @@ -23,14 +25,12 @@ export class W3mAccountTokensWidget extends LitElement { }) ] ) + this.watchConvertValues() } public override disconnectedCallback() { this.unsubscribe.forEach(unsubscribe => unsubscribe()) - } - - public override firstUpdated() { - AccountController.fetchTokenBalance() + clearInterval(this.watchTokenBalance) } // -- Render -------------------------------------------- // @@ -39,6 +39,10 @@ export class W3mAccountTokensWidget extends LitElement { } // -- Private ------------------------------------------- // + private watchConvertValues() { + this.watchTokenBalance = setInterval(() => AccountController.fetchTokenBalance(), 1000) + } + private tokenTemplate() { if (this.tokenBalance && this.tokenBalance?.length > 0) { return html` diff --git a/packages/scaffold/src/views/w3m-approve-transaction-view/index.ts b/packages/scaffold/src/views/w3m-approve-transaction-view/index.ts index 04e91c459a..c43fa219dd 100644 --- a/packages/scaffold/src/views/w3m-approve-transaction-view/index.ts +++ b/packages/scaffold/src/views/w3m-approve-transaction-view/index.ts @@ -57,7 +57,7 @@ export class W3mApproveTransactionView extends LitElement { this.iframe.style.width = `${PAGE_WIDTH}px` this.iframe.style.height = `${PAGE_HEIGHT}px` this.iframe.style.left = `calc(50% - ${PAGE_WIDTH / 2}px)` - this.iframe.style.top = `calc(50% - ${PAGE_HEIGHT / 2} + ${HEADER_HEIGHT / 2}px)` + this.iframe.style.top = `calc(50% - ${PAGE_HEIGHT / 2}px + ${HEADER_HEIGHT / 2}px)` this.ready = true }) this.bodyObserver.observe(window.document.body) From 6b2ae0d159acd5dabab8f8b515ea12af1e326f4f Mon Sep 17 00:00:00 2001 From: enesozturk Date: Tue, 2 Apr 2024 20:35:51 +0300 Subject: [PATCH 58/96] chore: build issues --- packages/scaffold/src/views/w3m-convert-view/index.ts | 2 +- packages/solana/src/client.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/scaffold/src/views/w3m-convert-view/index.ts b/packages/scaffold/src/views/w3m-convert-view/index.ts index 6201da4c22..93b4f67bc2 100644 --- a/packages/scaffold/src/views/w3m-convert-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-view/index.ts @@ -214,7 +214,7 @@ export class W3mConvertView extends LitElement { >` } - private onSetMaxValue(target: Target, balance: string | undefined, address: string | undefined) { + private onSetMaxValue(target: Target, balance: string | undefined) { const token = target === 'sourceToken' ? this.sourceToken : this.toToken const isNetworkToken = token?.address === ConstantsUtil.NATIVE_TOKEN_ADDRESS diff --git a/packages/solana/src/client.ts b/packages/solana/src/client.ts index 327ab91223..5e5c3b9dc3 100644 --- a/packages/solana/src/client.ts +++ b/packages/solana/src/client.ts @@ -90,6 +90,7 @@ export class Web3Modal extends Web3ModalScaffold { }) } + // @ts-ignore const connectionControllerClient: ConnectionControllerClient = { connectWalletConnect: async onUri => { const WalletConnectProvider = await this.WalletConnectConnector.getProvider() From 678fdb392ccad17588e697a76b49a427ffd73cb2 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Wed, 3 Apr 2024 12:57:25 +0300 Subject: [PATCH 59/96] chore: code review updates --- .../src/controllers/ConvertApiController.ts | 73 ++++++--------- .../core/src/controllers/ConvertController.ts | 12 +-- .../core/src/controllers/RouterController.ts | 1 - packages/core/src/utils/CoreHelperUtil.ts | 6 -- packages/scaffold/index.ts | 1 - .../scaffold/src/modal/w3m-router/index.ts | 2 - .../scaffold/src/partials/w3m-header/index.ts | 1 - .../w3m-convert-select-network-view/index.ts | 88 ------------------- .../w3m-convert-select-network-view/styles.ts | 82 ----------------- 9 files changed, 31 insertions(+), 235 deletions(-) delete mode 100644 packages/scaffold/src/views/w3m-convert-select-network-view/index.ts delete mode 100644 packages/scaffold/src/views/w3m-convert-select-network-view/styles.ts diff --git a/packages/core/src/controllers/ConvertApiController.ts b/packages/core/src/controllers/ConvertApiController.ts index aa113f849a..1f9484a860 100644 --- a/packages/core/src/controllers/ConvertApiController.ts +++ b/packages/core/src/controllers/ConvertApiController.ts @@ -8,19 +8,20 @@ import { ConstantsUtil } from '../utils/ConstantsUtil.js' const ONEINCH_API_BASE_URL = 'https://1inch-swap-proxy.walletconnect-v1-bridge.workers.dev' export const DEFAULT_SLIPPAGE_TOLERANCE = '0.5' -const OneInchAPIEndpoints = { - approveTransaction: (chainId: number) => `/swap/v5.2/${chainId}/approve/transaction`, - approveAllowance: (chainId: number) => `/swap/v5.2/${chainId}/approve/allowance`, - gas: (chainId: number) => `/gas-price/v1.5/${chainId}`, - gasPrice: (chainId: number) => `/gas-price/v1.5/${chainId}`, - swap: (chainId: number) => `/swap/v5.2/${chainId}/swap`, - tokens: (chainId: number) => `/swap/v5.2/${chainId}/tokens`, - tokensCustom: (chainId: number) => `/token/v1.2/${chainId}/custom`, - tokensPrices: (chainId: number) => `/price/v1.1/${chainId}`, - search: (chainId: number) => `/token/v1.2/${chainId}/search`, - balance: (chainId: number, address: string | undefined) => - `/balance/v1.2/${chainId}/balances/${address}`, - quote: (chainId: number) => `/swap/v6.0/${chainId}/quote` +function get1InchEndpoints(chainId: number, address: string | undefined) { + return { + approveTransaction: `/swap/v5.2/${chainId}/approve/transaction`, + approveAllowance: `/swap/v5.2/${chainId}/approve/allowance`, + gas: `/gas-price/v1.5/${chainId}`, + gasPrice: `/gas-price/v1.5/${chainId}`, + swap: `/swap/v5.2/${chainId}/swap`, + tokens: `/swap/v5.2/${chainId}/tokens`, + tokensCustom: `/token/v1.2/${chainId}/custom`, + tokensPrices: `/price/v1.1/${chainId}`, + search: `/token/v1.2/${chainId}/search`, + balance: `/balance/v1.2/${chainId}/balances/${address}`, + quote: `/swap/v6.0/${chainId}/quote` + } } // -- Types --------------------------------------------- // @@ -117,37 +118,26 @@ export const ConvertApiController = { const chainId = CoreHelperUtil.getEvmChainId(NetworkController.state.caipNetwork?.id) const { address } = AccountController.state + const endpoints = get1InchEndpoints(chainId, address) + return { api, paths: { - approveTransaction: OneInchAPIEndpoints.approveTransaction(chainId), - approveAllowance: OneInchAPIEndpoints.approveAllowance(chainId), - gas: OneInchAPIEndpoints.gasPrice(chainId), - gasPrice: OneInchAPIEndpoints.gasPrice(chainId), - swap: OneInchAPIEndpoints.swap(chainId), - tokens: OneInchAPIEndpoints.tokens(chainId), - tokensCustom: OneInchAPIEndpoints.tokensCustom(chainId), - tokenPrices: OneInchAPIEndpoints.tokensPrices(chainId), - search: OneInchAPIEndpoints.search(chainId), - balance: OneInchAPIEndpoints.balance(chainId, address), - quote: OneInchAPIEndpoints.quote(chainId) + approveTransaction: endpoints.approveTransaction, + approveAllowance: endpoints.approveAllowance, + gas: endpoints.gasPrice, + gasPrice: endpoints.gasPrice, + swap: endpoints.swap, + tokens: endpoints.tokens, + tokensCustom: endpoints.tokensCustom, + tokenPrices: endpoints.tokensPrices, + search: endpoints.search, + balance: endpoints.balance, + quote: endpoints.quote } } }, - _getSwapParams() { - const { address } = AccountController.state - if (!address) { - throw new Error('No address found to swap the tokens from.') - } - - return { - fromAddress: address as `0x${string}`, - slippage: DEFAULT_SLIPPAGE_TOLERANCE - } - }, - - // /gas-price/v1.5/${chainId} async getGasPrice() { const { api, paths } = this.get1InchAPI() @@ -159,7 +149,6 @@ export const ConvertApiController = { return gasPrices }, - // - /swap/v5.2/${chainId}/approve/allowance async checkConvertAllowance({ fromAddress, sourceTokenAddress, @@ -183,14 +172,12 @@ export const ConvertApiController = { return false }, - // - /swap/v5.2/${chainId}/tokens async getTokenList() { const { api, paths } = this.get1InchAPI() return await api.get({ path: paths.tokens }) }, - // - /token/v1.2/${chainId}/search async searchTokens(searchTerm: string) { const { api, paths } = this.get1InchAPI() @@ -223,7 +210,6 @@ export const ConvertApiController = { return mergedTokensWithBalances }, - // - /balance/v1.2/${chainId}/balances/${address} async getBalances() { const { api, paths } = this.get1InchAPI() @@ -245,7 +231,6 @@ export const ConvertApiController = { return { balances: nonEmptyBalances, tokenAddresses: Object.keys(nonEmptyBalances) } }, - // - /token/v1.2/${chainId}/custom async getTokenInfoWithAddresses(addresses: string[]) { const { api, paths } = this.get1InchAPI() @@ -255,7 +240,6 @@ export const ConvertApiController = { }) }, - // - /price/v1.1/${chainId} async getTokenPriceWithAddresses(addresses: string[]) { const { api, paths } = this.get1InchAPI() @@ -292,7 +276,6 @@ export const ConvertApiController = { return mergedTokens }, - // --- /swap/v5.2/${chainId}/swap async getConvertData({ sourceTokenAddress, toTokenAddress, @@ -314,7 +297,6 @@ export const ConvertApiController = { }) }, - // --- /swap/v5.2/${chainId}/approve/transaction async getConvertApprovalData({ sourceTokenAddress }: GetApprovalParams) { const { api, paths } = this.get1InchAPI() @@ -326,7 +308,6 @@ export const ConvertApiController = { }) }, - // --- /swap/v5.2/${chainId}/approve/transaction async getQuoteApprovalData({ sourceTokenAddress, toTokenAddress, diff --git a/packages/core/src/controllers/ConvertController.ts b/packages/core/src/controllers/ConvertController.ts index 4475add17f..799b926ac1 100644 --- a/packages/core/src/controllers/ConvertController.ts +++ b/packages/core/src/controllers/ConvertController.ts @@ -378,15 +378,11 @@ export const ConvertController = { }, calculateGasPriceInUSD(gas: bigint, gasPrice: bigint) { - try { - const totalGasCostInEther = this.calculateGasPriceInEther(gas, gasPrice) - const networkPriceNumber = NumberUtil.bigNumber(state.networkPrice) - const totalCostInUSD = networkPriceNumber.multipliedBy(totalGasCostInEther) + const totalGasCostInEther = this.calculateGasPriceInEther(gas, gasPrice) + const networkPriceInUSD = NumberUtil.bigNumber(state.networkPrice) + const gasCostInUSD = networkPriceInUSD.multipliedBy(totalGasCostInEther) - return totalCostInUSD.toNumber() - } catch (error) { - return 0 - } + return gasCostInUSD.toNumber() }, calculatePriceImpact(_toTokenAmount: string, _gasPriceInUSD = 0) { diff --git a/packages/core/src/controllers/RouterController.ts b/packages/core/src/controllers/RouterController.ts index e766299528..4547431ca8 100644 --- a/packages/core/src/controllers/RouterController.ts +++ b/packages/core/src/controllers/RouterController.ts @@ -46,7 +46,6 @@ export interface RouterControllerState { | 'WhatIsABuy' | 'Convert' | 'ConvertSelectToken' - | 'ConvertSelectNetwork' | 'ConvertPreview' history: RouterControllerState['view'][] data?: { diff --git a/packages/core/src/utils/CoreHelperUtil.ts b/packages/core/src/utils/CoreHelperUtil.ts index 7d47d2f00a..e5a99c1f57 100644 --- a/packages/core/src/utils/CoreHelperUtil.ts +++ b/packages/core/src/utils/CoreHelperUtil.ts @@ -277,12 +277,6 @@ export const CoreHelperUtil = { return { dollars, pennies } }, - get1inchApiUrl(chainId: number) { - return CoreHelperUtil.isRestrictedRegion() - ? `https://swap.walletconnect.org/${chainId}` - : `https://swap.walletconnect.com/${chainId}` - }, - isAddress(address: string): boolean { if (!/^(?:0x)?[0-9a-f]{40}$/iu.test(address)) { return false diff --git a/packages/scaffold/index.ts b/packages/scaffold/index.ts index 0bef8a5864..485a75a5c5 100644 --- a/packages/scaffold/index.ts +++ b/packages/scaffold/index.ts @@ -23,7 +23,6 @@ export * from './src/views/w3m-onramp-providers-view/index.js' export * from './src/views/w3m-onramp-tokens-select-view/index.js' export * from './src/views/w3m-convert-view/index.js' export * from './src/views/w3m-convert-preview-view/index.js' -export * from './src/views/w3m-convert-select-network-view/index.js' export * from './src/views/w3m-convert-select-token-view/index.js' export * from './src/views/w3m-convert-view/index.js' export * from './src/views/w3m-transactions-view/index.js' diff --git a/packages/scaffold/src/modal/w3m-router/index.ts b/packages/scaffold/src/modal/w3m-router/index.ts index 6f9d985a2f..44928a9a43 100644 --- a/packages/scaffold/src/modal/w3m-router/index.ts +++ b/packages/scaffold/src/modal/w3m-router/index.ts @@ -121,8 +121,6 @@ export class W3mRouter extends LitElement { return html`` case 'ConvertSelectToken': return html`` - case 'ConvertSelectNetwork': - return html`` case 'ConvertPreview': return html`` case 'WalletSend': diff --git a/packages/scaffold/src/partials/w3m-header/index.ts b/packages/scaffold/src/partials/w3m-header/index.ts index 160e6d5557..d3ddef2108 100644 --- a/packages/scaffold/src/partials/w3m-header/index.ts +++ b/packages/scaffold/src/partials/w3m-header/index.ts @@ -55,7 +55,6 @@ function headings() { WalletCompatibleNetworks: 'Compatible Networks', Convert: 'Convert', ConvertSelectToken: 'Select token', - ConvertSelectNetwork: 'Select network', ConvertPreview: 'Preview convert', WalletSend: 'Send', WalletSendPreview: 'Review send', diff --git a/packages/scaffold/src/views/w3m-convert-select-network-view/index.ts b/packages/scaffold/src/views/w3m-convert-select-network-view/index.ts deleted file mode 100644 index 60bf156ba0..0000000000 --- a/packages/scaffold/src/views/w3m-convert-select-network-view/index.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { customElement } from '@web3modal/ui' -import { LitElement, html } from 'lit' -import styles from './styles.js' -import { state } from 'lit/decorators.js' -import { NetworkController } from '@web3modal/core' - -const yourItems = [ - { - name: 'Ethereum', - symbol: 'ETH' - }, - { - name: 'Avalanche', - symbol: 'AVAX' - }, - { - name: 'Bitcoin', - symbol: 'BTC' - }, - { - name: 'Cardano', - symbol: 'ADA' - }, - { - name: 'Ripple', - symbol: 'XRP' - } -] - -@customElement('w3m-convert-select-network-view') -export class W3mConvertSelectNetworkView extends LitElement { - public static override styles = styles - // -- Members ------------------------------------------- // - private unsubscribe: (() => void)[] = [] - - // -- State & Properties -------------------------------- // - @state() private network = NetworkController.state.caipNetwork - - // -- Lifecycle ----------------------------------------- // - public constructor() { - super() - this.unsubscribe.push( - ...[NetworkController.subscribeKey('caipNetwork', val => (this.network = val))] - ) - } - - public override disconnectedCallback() { - this.unsubscribe.forEach(unsubscribe => unsubscribe()) - } - - // -- Render -------------------------------------------- // - public override render() { - return html` - ${this.templateListNetworks()} - ` - } - - // -- Private ------------------------------------------- // - private templateListNetworks() { - return html` - - - ${yourItems.map( - item => html`${this.templateNetworkListItem(item, this.network?.name === item.name)}` - )} - - - ` - } - - private templateNetworkListItem(item: { name: string; symbol: string }, active?: boolean) { - return html`` - } -} - -declare global { - interface HTMLElementTagNameMap { - 'w3m-convert-select-network-view': W3mConvertSelectNetworkView - } -} diff --git a/packages/scaffold/src/views/w3m-convert-select-network-view/styles.ts b/packages/scaffold/src/views/w3m-convert-select-network-view/styles.ts deleted file mode 100644 index 1ec529bdf0..0000000000 --- a/packages/scaffold/src/views/w3m-convert-select-network-view/styles.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { css } from 'lit' - -export default css` - :host > wui-flex:first-child { - overflow-y: hidden; - overflow-x: hidden; - scrollbar-width: none; - } - - wui-loading-hexagon { - position: absolute; - } - - .network-list-item { - background: none; - border: none; - display: flex; - align-items: center; - justify-content: space-between; - gap: var(--wui-spacing-s); - padding: var(--wui-spacing-s); - border-radius: var(--wui-border-radius-xs); - outline: none; - } - - .network-list-item:hover { - background: var(--wui-gray-glass-001); - } - - .network-list-item:focus-visible { - box-shadow: inset 0 0 0 2px var(--wui-gray-glass-010); - } - - .network-list-item.active { - background: var(--wui-gray-glass-002); - } - - .network-list-item wui-image { - width: 40px; - height: 40px; - border-radius: var(--wui-border-radius-s); - box-shadow: inset 0 0 0 2px var(--wui-gray-glass-010); - } - - .network-list-item wui-icon { - width: 14px; - height: 14px; - } - - .token-list { - padding-top: var(--wui-spacing-s); - max-height: calc(512px); - overflow-y: auto; - border-top-left-radius: 30px; - border-top-right-radius: 30px; - } - - .network-search-input, - .select-network-button { - height: 40px; - } - - .select-network-button { - box-shadow: inset 0 0 0 1px var(--wui-gray-glass-005); - background-color: var(--wui-gray-glass-002); - border-radius: var(--wui-border-radius-xxs); - padding: var(--wui-spacing-xs); - align-items: center; - transition: background-color 0.2s linear; - } - - .select-network-button:hover { - background-color: var(--wui-gray-glass-005); - } - - .select-network-button > wui-image { - width: 26px; - height: 26px; - border-radius: var(--wui-border-radius-xs); - box-shadow: inset 0 0 0 1px var(--wui-gray-glass-010); - } -` From befc141ca3c3b1f342d44a8b12b02d345c0c9d7e Mon Sep 17 00:00:00 2001 From: enesozturk Date: Wed, 3 Apr 2024 13:13:56 +0300 Subject: [PATCH 60/96] chore: move convert api to utils, export missing comps --- package-lock.json | 2 +- packages/common/package.json | 2 +- packages/core/index.ts | 2 +- .../core/src/controllers/ConvertController.ts | 18 +++++++++--------- .../ConvertApiUtil.ts} | 2 +- .../src/views/w3m-convert-view/index.ts | 2 +- .../src/composites/wui-details-group/styles.ts | 1 + .../composites/wui-token-list-item/styles.ts | 1 + packages/ui/src/utils/JSXTypeUtil.ts | 4 ++++ 9 files changed, 20 insertions(+), 14 deletions(-) rename packages/core/src/{controllers/ConvertApiController.ts => utils/ConvertApiUtil.ts} (99%) diff --git a/package-lock.json b/package-lock.json index 23d193b8a6..09e3098814 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31392,7 +31392,7 @@ "version": "4.1.3-5f2ae345.1", "license": "Apache-2.0", "dependencies": { - "bignumber.js": "^9.1.2", + "bignumber.js": "9.1.2", "dayjs": "1.11.10" } }, diff --git a/packages/common/package.json b/packages/common/package.json index fdf9fd888c..93525396ba 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -17,7 +17,7 @@ "lint": "eslint . --ext .js,.jsx,.ts,.tsx" }, "dependencies": { - "bignumber.js": "^9.1.2", + "bignumber.js": "9.1.2", "dayjs": "1.11.10" }, "keywords": [ diff --git a/packages/core/index.ts b/packages/core/index.ts index c428ec6e8b..45a9305273 100644 --- a/packages/core/index.ts +++ b/packages/core/index.ts @@ -55,7 +55,7 @@ export type { EventsControllerState } from './src/controllers/EventsController.j export { TransactionsController } from './src/controllers/TransactionsController.js' export type { TransactionsControllerState } from './src/controllers/TransactionsController.js' -export { ConvertApiController } from './src/controllers/ConvertApiController.js' +export { ConvertApi } from './src/controllers/ConvertApiUtil.js' export { ConvertController } from './src/controllers/ConvertController.js' export type { ConvertControllerState } from './src/controllers/ConvertController.js' diff --git a/packages/core/src/controllers/ConvertController.ts b/packages/core/src/controllers/ConvertController.ts index 799b926ac1..5624cece8c 100644 --- a/packages/core/src/controllers/ConvertController.ts +++ b/packages/core/src/controllers/ConvertController.ts @@ -3,7 +3,7 @@ import { proxy, subscribe as sub } from 'valtio/vanilla' import { AccountController } from './AccountController.js' import { ConstantsUtil } from '../utils/ConstantsUtil.js' import { ConnectionController } from './ConnectionController.js' -import { ConvertApiController } from './ConvertApiController.js' +import { ConvertApiUtil } from '../utils/ConvertApiUtil.js' import { SnackController } from './SnackController.js' import { RouterController } from './RouterController.js' import { NumberUtil } from '@web3modal/common' @@ -268,7 +268,7 @@ export const ConvertController = { }, async getTokenList() { - const res = await ConvertApiController.getTokenList() + const res = await ConvertApiUtil.getTokenList() state.tokens = res.tokens state.popularTokens = Object.entries(res.tokens) @@ -310,7 +310,7 @@ export const ConvertController = { if (existPrice) { return parseFloat(existPrice) } - const prices = await ConvertApiController.getTokenPriceWithAddresses([address]) + const prices = await ConvertApiUtil.getTokenPriceWithAddresses([address]) const price = prices[address] || '0' state.tokensPriceMap[address] = price @@ -318,7 +318,7 @@ export const ConvertController = { }, async getNetworkTokenPrice() { - const prices = await ConvertApiController.getTokenPriceWithAddresses([ + const prices = await ConvertApiUtil.getTokenPriceWithAddresses([ ConstantsUtil.NATIVE_TOKEN_ADDRESS ]) const price = prices[ConstantsUtil.NATIVE_TOKEN_ADDRESS] || '0' @@ -327,7 +327,7 @@ export const ConvertController = { }, async getMyTokensWithBalance() { - const res = await ConvertApiController.getMyTokensWithBalance() + const res = await ConvertApiUtil.getMyTokensWithBalance() if (!res) { return @@ -352,7 +352,7 @@ export const ConvertController = { }, async getInitialGasPrice() { - const res = await ConvertApiController.getGasPrice() + const res = await ConvertApiUtil.getGasPrice() const instant = res.instant const value = typeof instant === 'object' ? res.instant.maxFeePerGas : instant const gasFee = BigInt(value) @@ -447,7 +447,7 @@ export const ConvertController = { return null } - const hasAllowance = await ConvertApiController.checkConvertAllowance({ + const hasAllowance = await ConvertApiUtil.checkConvertAllowance({ fromAddress, sourceTokenAddress, sourceTokenAmount, @@ -492,7 +492,7 @@ export const ConvertController = { throw new Error('>>> createTokenAllowance - No source token address found.') } - const transaction = await ConvertApiController.getConvertApprovalData({ + const transaction = await ConvertApiUtil.getConvertApprovalData({ sourceTokenAddress }) @@ -556,7 +556,7 @@ export const ConvertController = { } try { - const response = await ConvertApiController.getConvertData({ + const response = await ConvertApiUtil.getConvertData({ fromAddress, sourceTokenAddress, sourceTokenAmount, diff --git a/packages/core/src/controllers/ConvertApiController.ts b/packages/core/src/utils/ConvertApiUtil.ts similarity index 99% rename from packages/core/src/controllers/ConvertApiController.ts rename to packages/core/src/utils/ConvertApiUtil.ts index 1f9484a860..47023e0f2d 100644 --- a/packages/core/src/controllers/ConvertApiController.ts +++ b/packages/core/src/utils/ConvertApiUtil.ts @@ -112,7 +112,7 @@ export type GetGasPricesResponse = { } // -- Controller ---------------------------------------- // -export const ConvertApiController = { +export const ConvertApiUtil = { get1InchAPI() { const api = new FetchUtil({ baseUrl: ONEINCH_API_BASE_URL }) const chainId = CoreHelperUtil.getEvmChainId(NetworkController.state.caipNetwork?.id) diff --git a/packages/scaffold/src/views/w3m-convert-view/index.ts b/packages/scaffold/src/views/w3m-convert-view/index.ts index 93b4f67bc2..bea11507ee 100644 --- a/packages/scaffold/src/views/w3m-convert-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-view/index.ts @@ -11,7 +11,7 @@ import { ConstantsUtil } from '@web3modal/core' import { NumberUtil } from '@web3modal/common' -import type { TokenInfo } from '@web3modal/core/src/controllers/ConvertApiController.js' +import type { TokenInfo } from '@web3modal/core/src/controllers/ConvertApiUtil.js' type Target = 'sourceToken' | 'toToken' diff --git a/packages/ui/src/composites/wui-details-group/styles.ts b/packages/ui/src/composites/wui-details-group/styles.ts index 9995b03b71..2bd7881ffa 100644 --- a/packages/ui/src/composites/wui-details-group/styles.ts +++ b/packages/ui/src/composites/wui-details-group/styles.ts @@ -2,6 +2,7 @@ import { css } from 'lit' export default css` :host { + display: block; padding: var(--wui-spacing-l) var(--wui-spacing-m); background-color: var(--wui-gray-glass-002); border-radius: var(--wui-border-radius-xs); diff --git a/packages/ui/src/composites/wui-token-list-item/styles.ts b/packages/ui/src/composites/wui-token-list-item/styles.ts index 488ecfd1c0..8e5f1897a3 100644 --- a/packages/ui/src/composites/wui-token-list-item/styles.ts +++ b/packages/ui/src/composites/wui-token-list-item/styles.ts @@ -3,6 +3,7 @@ import { css } from 'lit' export default css` :host > wui-flex { cursor: pointer; + display: block; column-gap: var(--wui-spacing-s); padding: var(--wui-spacing-xs); padding-right: var(--wui-spacing-l); diff --git a/packages/ui/src/utils/JSXTypeUtil.ts b/packages/ui/src/utils/JSXTypeUtil.ts index 57dec7391a..effabe86b5 100644 --- a/packages/ui/src/utils/JSXTypeUtil.ts +++ b/packages/ui/src/utils/JSXTypeUtil.ts @@ -15,6 +15,8 @@ import type { WuiButton } from '../composites/wui-button/index.js' import type { WuiCardSelectLoader } from '../composites/wui-card-select-loader/index.js' import type { WuiCardSelect } from '../composites/wui-card-select/index.js' import type { WuiChip } from '../composites/wui-chip/index.js' +import type { WuiConvertInput } from '../composites/wui-convert-input/index.js' +import type { WuiConvertDetails } from '../composites/wui-convert-details/index.js' import type { WuiConnectButton } from '../composites/wui-connect-button/index.js' import type { WuiCtaButton } from '../composites/wui-cta-button/index.js' import type { WuiDetailsGroup } from '../composites/wui-details-group/index.js' @@ -87,6 +89,8 @@ declare global { 'wui-card-select-loader': CustomElement 'wui-card-select': CustomElement 'wui-chip': CustomElement + 'wui-convert-input': CustomElement + 'wui-convert-details': CustomElement 'wui-connect-button': CustomElement 'wui-cta-button': CustomElement 'wui-details-group': CustomElement From 4712c6a06d8527fe888a925f6dc6b19bc0cfc2f0 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Wed, 3 Apr 2024 13:27:43 +0300 Subject: [PATCH 61/96] fix: imports --- packages/core/index.ts | 2 -- packages/core/src/utils/ConvertApiUtil.ts | 6 +++--- packages/scaffold/src/views/w3m-convert-view/index.ts | 2 +- packages/wagmi/src/client.ts | 4 ---- 4 files changed, 4 insertions(+), 10 deletions(-) diff --git a/packages/core/index.ts b/packages/core/index.ts index 45a9305273..3d3720acdb 100644 --- a/packages/core/index.ts +++ b/packages/core/index.ts @@ -55,8 +55,6 @@ export type { EventsControllerState } from './src/controllers/EventsController.j export { TransactionsController } from './src/controllers/TransactionsController.js' export type { TransactionsControllerState } from './src/controllers/TransactionsController.js' -export { ConvertApi } from './src/controllers/ConvertApiUtil.js' - export { ConvertController } from './src/controllers/ConvertController.js' export type { ConvertControllerState } from './src/controllers/ConvertController.js' diff --git a/packages/core/src/utils/ConvertApiUtil.ts b/packages/core/src/utils/ConvertApiUtil.ts index 47023e0f2d..0338fee964 100644 --- a/packages/core/src/utils/ConvertApiUtil.ts +++ b/packages/core/src/utils/ConvertApiUtil.ts @@ -1,8 +1,8 @@ import { CoreHelperUtil } from '../utils/CoreHelperUtil.js' -import { NetworkController } from './NetworkController.js' +import { NetworkController } from '../controllers/NetworkController.js' import { FetchUtil } from '../utils/FetchUtil.js' -import { AccountController } from './AccountController.js' -import { ConnectionController } from './ConnectionController.js' +import { AccountController } from '../controllers/AccountController.js' +import { ConnectionController } from '../controllers/ConnectionController.js' import { ConstantsUtil } from '../utils/ConstantsUtil.js' const ONEINCH_API_BASE_URL = 'https://1inch-swap-proxy.walletconnect-v1-bridge.workers.dev' diff --git a/packages/scaffold/src/views/w3m-convert-view/index.ts b/packages/scaffold/src/views/w3m-convert-view/index.ts index bea11507ee..c2ea27a6cc 100644 --- a/packages/scaffold/src/views/w3m-convert-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-view/index.ts @@ -11,7 +11,7 @@ import { ConstantsUtil } from '@web3modal/core' import { NumberUtil } from '@web3modal/common' -import type { TokenInfo } from '@web3modal/core/src/controllers/ConvertApiUtil.js' +import type { TokenInfo } from '@web3modal/core/src/utils/ConvertApiUtil.js' type Target = 'sourceToken' | 'toToken' diff --git a/packages/wagmi/src/client.ts b/packages/wagmi/src/client.ts index d8fe0cbecb..cb46d58602 100644 --- a/packages/wagmi/src/client.ts +++ b/packages/wagmi/src/client.ts @@ -470,8 +470,6 @@ export class Web3Modal extends Web3ModalScaffold { } provider.onRpcRequest(request => { - console.log('>>> [onRpcRequest]: ', request, RouterController.state.transactionSuccessStack) - if (W3mFrameHelpers.checkIfRequestExists(request)) { if (!W3mFrameHelpers.checkIfRequestIsAllowed(request)) { if (ModalController.state.open) { @@ -495,7 +493,6 @@ export class Web3Modal extends Web3ModalScaffold { }) provider.onRpcResponse(receive => { - console.log('>>> [onRpcResponse]: ', receive) // @ts-ignore const payload = receive?.payload // @ts-ignore @@ -510,7 +507,6 @@ export class Web3Modal extends Web3ModalScaffold { const isCompleted = isAddress && payload?.length > 10 if (isCompleted && RouterController.state.transactionSuccessStack?.length > 0) { - console.log('>>> [onRpcResponse]: RouterController.popTransactionSuccessAction()') RouterController.popTransactionSuccessAction() } }) From 17676777b13c2e878b99a64917dee00b6473b412 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Wed, 3 Apr 2024 13:29:26 +0300 Subject: [PATCH 62/96] Revert "Merge branch 'refactor/lab-ui-update-design-system-vars' into feat/1inch-swaps" This reverts commit ed9e43420d024d097788aa76451a32f07241bf0d, reversing changes made to ddcdaa0b5901435850f374c4c705c7335d620e3f. --- apps/laboratory/components.json | 17 - apps/laboratory/globals.css | 80 --- apps/laboratory/package.json | 17 +- apps/laboratory/postcss.config.js | 6 - .../Ethers/EthersSignMessageTest.tsx | 20 +- .../Ethers/EthersSignTypedDataTest.tsx | 15 +- .../src/components/Ethers/EthersTests.tsx | 66 ++- .../Ethers/EthersTransactionTest.tsx | 53 +- .../Ethers/EthersWriteContractTest.tsx | 53 +- apps/laboratory/src/components/Header.tsx | 53 -- .../src/components/Siwe/SiweData.tsx | 65 ++- .../Solana/SolanaSendTransactionTest.tsx | 41 +- .../Solana/SolanaSignMessageTest.tsx | 27 +- .../Solana/SolanaSignTransactionTest.tsx | 41 +- .../src/components/Solana/SolanaTests.tsx | 66 ++- .../AccentColorInput.tsx | 0 .../components/Theming/BorderRadiusInput.tsx | 48 ++ .../MixColorInput.tsx | 58 ++- .../RadioColor.tsx | 21 +- .../components/Wagmi/WagmiSendUSDCTest.tsx | 48 +- .../components/Wagmi/WagmiSignMessageTest.tsx | 25 +- .../Wagmi/WagmiSignTypedDataTest.tsx | 16 +- .../src/components/Wagmi/WagmiTests.tsx | 60 ++- .../components/Wagmi/WagmiTransactionTest.tsx | 68 +-- .../Wagmi/WagmiWriteContractTest.tsx | 92 ++-- .../src/components/Web3ModalButtons.tsx | 44 +- .../BorderRadiusInput.tsx | 29 -- .../components/configuration-dialog/index.tsx | 52 -- .../src/components/icons/ethers.tsx | 12 - .../src/components/icons/solana.tsx | 18 - .../laboratory/src/components/icons/wagmi.tsx | 19 - .../src/components/theme-provider.tsx | 10 - .../src/components/theme-toggle.tsx | 40 -- apps/laboratory/src/components/ui/button.tsx | 50 -- apps/laboratory/src/components/ui/card.tsx | 56 -- apps/laboratory/src/components/ui/column.tsx | 12 - apps/laboratory/src/components/ui/dialog.tsx | 114 ----- .../src/components/ui/dropdown-menu.tsx | 196 ------- apps/laboratory/src/components/ui/input.tsx | 22 - apps/laboratory/src/components/ui/row.tsx | 12 - apps/laboratory/src/components/ui/slider.tsx | 25 - .../src/components/ui/typography.tsx | 104 ---- apps/laboratory/src/layout/LayoutHeader.tsx | 46 ++ apps/laboratory/src/layout/OptionsDrawer.tsx | 86 ++++ apps/laboratory/src/layout/index.tsx | 16 + apps/laboratory/src/lib/utils.ts | 6 - apps/laboratory/src/pages/_app.tsx | 31 +- apps/laboratory/src/pages/_document.tsx | 5 +- apps/laboratory/src/pages/index.tsx | 147 +++--- apps/laboratory/tailwind.config.js | 72 --- apps/laboratory/tsconfig.json | 5 +- package-lock.json | 481 ++---------------- packages/ui/src/utils/ThemeUtil.ts | 14 +- 53 files changed, 830 insertions(+), 1950 deletions(-) delete mode 100644 apps/laboratory/components.json delete mode 100644 apps/laboratory/globals.css delete mode 100644 apps/laboratory/postcss.config.js delete mode 100644 apps/laboratory/src/components/Header.tsx rename apps/laboratory/src/components/{configuration-dialog => Theming}/AccentColorInput.tsx (100%) create mode 100644 apps/laboratory/src/components/Theming/BorderRadiusInput.tsx rename apps/laboratory/src/components/{configuration-dialog => Theming}/MixColorInput.tsx (51%) rename apps/laboratory/src/components/{configuration-dialog => Theming}/RadioColor.tsx (52%) delete mode 100644 apps/laboratory/src/components/configuration-dialog/BorderRadiusInput.tsx delete mode 100644 apps/laboratory/src/components/configuration-dialog/index.tsx delete mode 100644 apps/laboratory/src/components/icons/ethers.tsx delete mode 100644 apps/laboratory/src/components/icons/solana.tsx delete mode 100644 apps/laboratory/src/components/icons/wagmi.tsx delete mode 100644 apps/laboratory/src/components/theme-provider.tsx delete mode 100644 apps/laboratory/src/components/theme-toggle.tsx delete mode 100644 apps/laboratory/src/components/ui/button.tsx delete mode 100644 apps/laboratory/src/components/ui/card.tsx delete mode 100644 apps/laboratory/src/components/ui/column.tsx delete mode 100644 apps/laboratory/src/components/ui/dialog.tsx delete mode 100644 apps/laboratory/src/components/ui/dropdown-menu.tsx delete mode 100644 apps/laboratory/src/components/ui/input.tsx delete mode 100644 apps/laboratory/src/components/ui/row.tsx delete mode 100644 apps/laboratory/src/components/ui/slider.tsx delete mode 100644 apps/laboratory/src/components/ui/typography.tsx create mode 100644 apps/laboratory/src/layout/LayoutHeader.tsx create mode 100644 apps/laboratory/src/layout/OptionsDrawer.tsx create mode 100644 apps/laboratory/src/layout/index.tsx delete mode 100644 apps/laboratory/src/lib/utils.ts delete mode 100644 apps/laboratory/tailwind.config.js diff --git a/apps/laboratory/components.json b/apps/laboratory/components.json deleted file mode 100644 index 5c216e0e7a..0000000000 --- a/apps/laboratory/components.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "$schema": "https://ui.shadcn.com/schema.json", - "style": "default", - "rsc": true, - "tsx": true, - "tailwind": { - "config": "tailwind.config.js", - "css": "/src/global.css", - "baseColor": "slate", - "cssVariables": true, - "prefix": "" - }, - "aliases": { - "components": "@/components", - "utils": "@/lib/utils" - } -} diff --git a/apps/laboratory/globals.css b/apps/laboratory/globals.css deleted file mode 100644 index 23a3a392f5..0000000000 --- a/apps/laboratory/globals.css +++ /dev/null @@ -1,80 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -@layer base { - :root { - --background: 0 0% 100%; - --foreground: 222.2 84% 4.9%; - - --card: 0 0% 100%; - --card-foreground: 222.2 84% 4.9%; - - --popover: 0 0% 100%; - --popover-foreground: 222.2 84% 4.9%; - - --primary: 222.2 47.4% 11.2%; - --primary-foreground: 210 40% 98%; - - --secondary: 210 40% 96.1%; - --secondary-foreground: 222.2 47.4% 11.2%; - - --muted: 210 40% 96.1%; - --muted-foreground: 215.4 16.3% 46.9%; - - --accent: 210 40% 96.1%; - --accent-foreground: 222.2 47.4% 11.2%; - - --destructive: 0 84.2% 60.2%; - --destructive-foreground: 210 40% 98%; - - --border: 214.3 31.8% 91.4%; - --input: 214.3 31.8% 91.4%; - --ring: 222.2 84% 4.9%; - - --radius: 0.5rem; - } - - .dark { - --background: 240 10% 3.9%; - --foreground: 0 0% 98%; - - --card: 240 10% 3.9%; - --card-foreground: 0 0% 98%; - - --popover: 240 10% 3.9%; - --popover-foreground: 0 0% 98%; - - --primary: 0 0% 98%; - --primary-foreground: 240 5.9% 10%; - - --secondary: 240 3.7% 15.9%; - --secondary-foreground: 0 0% 98%; - - --muted: 240 3.7% 15.9%; - --muted-foreground: 240 5% 64.9%; - - --accent: 240 3.7% 15.9%; - --accent-foreground: 0 0% 98%; - - --destructive: 0 62.8% 30.6%; - --destructive-foreground: 0 0% 98%; - - --border: 240 3.7% 15.9%; - --input: 240 3.7% 15.9%; - - --ring: 240 4.9% 83.9%; - } -} - -@layer base { - * { - @apply border-border; - } - body { - @apply bg-background text-foreground; - } - w3m-onramp-widget { - width: 100%; - } -} diff --git a/apps/laboratory/package.json b/apps/laboratory/package.json index ecea5866b7..e34be4ddd0 100644 --- a/apps/laboratory/package.json +++ b/apps/laboratory/package.json @@ -5,7 +5,6 @@ "scripts": { "dev:laboratory": "next dev", "build:laboratory": "next build", - "format": "prettier --write .", "lint": "eslint . --ext .js,.jsx,.ts,.tsx", "playwright:start": "npm run dev:laboratory", "playwright:install": "playwright install --with-deps", @@ -26,11 +25,6 @@ "@chakra-ui/react": "2.8.2", "@emotion/react": "11.11.3", "@emotion/styled": "11.11.0", - "@radix-ui/react-dialog": "1.0.5", - "@radix-ui/react-dropdown-menu": "2.0.6", - "@radix-ui/react-icons": "1.3.0", - "@radix-ui/react-slider": "1.1.2", - "@radix-ui/react-slot": "1.0.2", "@sentry/browser": "7.92.0", "@sentry/react": "7.92.0", "@solana/web3.js": "1.87.6", @@ -39,16 +33,12 @@ "@web3modal/siwe": "4.1.3-5f2ae345.1", "@web3modal/solana": "4.1.3-5f2ae345.1", "@web3modal/wagmi": "4.1.3-5f2ae345.1", - "class-variance-authority": "0.7.0", "ethers": "6.9.0", "framer-motion": "10.17.9", "next": "14.0.4", "next-auth": "4.24.5", - "next-themes": "0.3.0", "react-icons": "4.12.0", "siwe": "2.1.4", - "sonner": "1.4.41", - "tailwindcss-animate": "1.0.7", "valtio": "1.11.2", "viem": "2.9.3", "wagmi": "2.5.7" @@ -57,11 +47,6 @@ "@aws-sdk/client-cloudwatch": "3.509.0", "@mailsac/api": "1.0.5", "@playwright/test": "1.40.1", - "autoprefixer": "10.4.19", - "clsx": "2.1.0", - "dotenv": "16.3.1", - "postcss": "8.4.38", - "tailwind-merge": "2.2.2", - "tailwindcss": "3.4.3" + "dotenv": "16.3.1" } } diff --git a/apps/laboratory/postcss.config.js b/apps/laboratory/postcss.config.js deleted file mode 100644 index 85f717cc09..0000000000 --- a/apps/laboratory/postcss.config.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - plugins: { - tailwindcss: {}, - autoprefixer: {} - } -} diff --git a/apps/laboratory/src/components/Ethers/EthersSignMessageTest.tsx b/apps/laboratory/src/components/Ethers/EthersSignMessageTest.tsx index 737f249644..a945d812f8 100644 --- a/apps/laboratory/src/components/Ethers/EthersSignMessageTest.tsx +++ b/apps/laboratory/src/components/Ethers/EthersSignMessageTest.tsx @@ -1,10 +1,10 @@ -import { toast } from 'sonner' +import { Button, useToast } from '@chakra-ui/react' import { useWeb3ModalAccount, useWeb3ModalProvider } from '@web3modal/ethers/react' import { BrowserProvider, JsonRpcSigner } from 'ethers' import { ConstantsUtil } from '../../utils/ConstantsUtil' -import { Button } from '@/components/ui/button' export function EthersSignMessageTest() { + const toast = useToast() const { address, chainId } = useWeb3ModalAccount() const { walletProvider } = useWeb3ModalProvider() @@ -16,18 +16,24 @@ export function EthersSignMessageTest() { const provider = new BrowserProvider(walletProvider, chainId) const signer = new JsonRpcSigner(provider, address) const signature = await signer?.signMessage('Hello Web3Modal Ethers') - toast.success(ConstantsUtil.SigningSucceededToastTitle, { - description: signature + toast({ + title: ConstantsUtil.SigningSucceededToastTitle, + description: signature, + status: 'success', + isClosable: true }) } catch { - toast.error(ConstantsUtil.SigningFailedToastTitle, { - description: 'Failed to sign message' + toast({ + title: ConstantsUtil.SigningFailedToastTitle, + description: 'Failed to sign message', + status: 'error', + isClosable: true }) } } return ( - ) diff --git a/apps/laboratory/src/components/Ethers/EthersSignTypedDataTest.tsx b/apps/laboratory/src/components/Ethers/EthersSignTypedDataTest.tsx index 8a6f30e818..1c91393a49 100644 --- a/apps/laboratory/src/components/Ethers/EthersSignTypedDataTest.tsx +++ b/apps/laboratory/src/components/Ethers/EthersSignTypedDataTest.tsx @@ -1,5 +1,4 @@ -import { Button } from '@/components/ui/button' -import { toast } from 'sonner' +import { Button, useToast } from '@chakra-ui/react' import { useWeb3ModalAccount, useWeb3ModalProvider } from '@web3modal/ethers/react' import { BrowserProvider, JsonRpcSigner } from 'ethers' import type { TypedDataField } from 'ethers' @@ -29,6 +28,7 @@ const message = { } as const export function EthersSignTypedDataTest() { + const toast = useToast() const { address, chainId } = useWeb3ModalAccount() const { walletProvider } = useWeb3ModalProvider() @@ -48,16 +48,19 @@ export function EthersSignTypedDataTest() { const signature = await signer?.signTypedData(domain, types, message) - toast.success('Succcess', { description: signature }) + toast({ title: 'Succcess', description: signature, status: 'success', isClosable: true }) } catch { - toast.error('Error', { - description: 'Failed to sign message' + toast({ + title: 'Error', + description: 'Failed to sign message', + status: 'error', + isClosable: true }) } } return ( - ) diff --git a/apps/laboratory/src/components/Ethers/EthersTests.tsx b/apps/laboratory/src/components/Ethers/EthersTests.tsx index de29869d8a..493d6ab283 100644 --- a/apps/laboratory/src/components/Ethers/EthersTests.tsx +++ b/apps/laboratory/src/components/Ethers/EthersTests.tsx @@ -1,35 +1,49 @@ +import { useWeb3ModalAccount } from '@web3modal/ethers/react' import { EthersSignMessageTest } from './EthersSignMessageTest' import { EthersSignTypedDataTest } from './EthersSignTypedDataTest' +import { StackDivider, Card, CardHeader, Heading, CardBody, Box, Stack } from '@chakra-ui/react' import { EthersTransactionTest } from './EthersTransactionTest' import { EthersWriteContractTest } from './EthersWriteContractTest' -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' -import { Column } from '@/components/ui/column' -import { Span } from '@/components/ui/typography' export function EthersTests() { - return ( - - - Test Interactions + const { isConnected } = useWeb3ModalAccount() + + return isConnected ? ( + + + Test Interactions - - - Sign Message - - - - Sign Typed Data - - - - Sign Transaction - - - - Contract Write - - - + + + } spacing="4"> + + + Sign Message + + + + + + + Sign Typed Data + + + + + + + Sign Transaction + + + + + + Contract Write + + + + + - ) + ) : null } diff --git a/apps/laboratory/src/components/Ethers/EthersTransactionTest.tsx b/apps/laboratory/src/components/Ethers/EthersTransactionTest.tsx index 69d3853a71..fe3c347d96 100644 --- a/apps/laboratory/src/components/Ethers/EthersTransactionTest.tsx +++ b/apps/laboratory/src/components/Ethers/EthersTransactionTest.tsx @@ -1,17 +1,12 @@ -import { toast } from 'sonner' +import { Button, useToast, Stack, Link, Text, Spacer } from '@chakra-ui/react' import { useWeb3ModalAccount, useWeb3ModalProvider } from '@web3modal/ethers/react' import { BrowserProvider, JsonRpcSigner, ethers } from 'ethers' import { sepolia, optimism } from '../../utils/ChainsUtil' import { useState } from 'react' import { vitalikEthAddress } from '../../utils/DataUtil' -import { Button, buttonVariants } from '@/components/ui/button' -import { cn } from '@/lib/utils' -import { Row } from '@/components/ui/row' -import { Column } from '@/components/ui/column' -import { Span } from '@/components/ui/typography' -import Link from 'next/link' export function EthersTransactionTest() { + const toast = useToast() const { address, chainId } = useWeb3ModalAccount() const { walletProvider } = useWeb3ModalProvider() const [loading, setLoading] = useState(false) @@ -28,10 +23,13 @@ export function EthersTransactionTest() { to: vitalikEthAddress, value: ethers.parseUnits('0.0001', 'gwei') }) - toast.success('Success', { description: tx.hash }) + toast({ title: 'Succcess', description: tx.hash, status: 'success', isClosable: true }) } catch { - toast.error('Error', { - description: 'Failed to sign transaction' + toast({ + title: 'Error', + description: 'Failed to sign transaction', + status: 'error', + isClosable: true }) } finally { setLoading(false) @@ -41,37 +39,32 @@ export function EthersTransactionTest() { const allowedChains = [sepolia.chainId, optimism.chainId] return allowedChains.includes(Number(chainId)) && address ? ( - + - - + + + + + - + + + + ) : ( - + Switch to Sepolia or OP to test this feature - + ) } diff --git a/apps/laboratory/src/components/Ethers/EthersWriteContractTest.tsx b/apps/laboratory/src/components/Ethers/EthersWriteContractTest.tsx index b7ee32ceff..760e5b0bc2 100644 --- a/apps/laboratory/src/components/Ethers/EthersWriteContractTest.tsx +++ b/apps/laboratory/src/components/Ethers/EthersWriteContractTest.tsx @@ -1,18 +1,13 @@ -import { toast } from 'sonner' +import { Button, useToast, Stack, Link, Text, Spacer } from '@chakra-ui/react' import { useWeb3ModalAccount, useWeb3ModalProvider } from '@web3modal/ethers/react' import { BrowserProvider, JsonRpcSigner, ethers } from 'ethers' import { optimism, sepolia } from '../../utils/ChainsUtil' import { useState } from 'react' import { abi, address as donutAddress } from '../../utils/DonutContract' -import { Column } from '@/components/ui/column' -import { Button, buttonVariants } from '@/components/ui/button' -import { Row } from '@/components/ui/row' -import Link from 'next/link' -import { cn } from '@/lib/utils' -import { Span } from '@/components/ui/typography' export function EthersWriteContractTest() { + const toast = useToast() const { address, chainId } = useWeb3ModalAccount() const { walletProvider } = useWeb3ModalProvider() const [loading, setLoading] = useState(false) @@ -28,10 +23,13 @@ export function EthersWriteContractTest() { const contract = new ethers.Contract(donutAddress, abi, signer) // @ts-expect-error ethers types are correct const tx = await contract.purchase(1, { value: ethers.parseEther('0.0001') }) - toast.success('Success', { description: tx.hash }) + toast({ title: 'Succcess', description: tx.hash, status: 'success', isClosable: true }) } catch { - toast.error('Error', { - description: 'Failed to sign transaction' + toast({ + title: 'Error', + description: 'Failed to sign transaction', + status: 'error', + isClosable: true }) } finally { setLoading(false) @@ -40,37 +38,32 @@ export function EthersWriteContractTest() { const allowedChains = [sepolia.chainId, optimism.chainId] return allowedChains.includes(Number(chainId)) && address ? ( - + - - + + + + + - + + + + ) : ( - + Switch to Sepolia or OP to test this feature - + ) } diff --git a/apps/laboratory/src/components/Header.tsx b/apps/laboratory/src/components/Header.tsx deleted file mode 100644 index d36b0ac185..0000000000 --- a/apps/laboratory/src/components/Header.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import Link from 'next/link' -import { Column } from '@/components/ui/column' -import Image from 'next/image' -import { Row } from '@/components/ui/row' -import { cn } from '@/lib/utils' -import { buttonVariants } from '@/components/ui/button' -import { GitHubLogoIcon, MixIcon, ReaderIcon } from '@radix-ui/react-icons' -import { ModeToggle } from '@/components/theme-toggle' -import { Span } from '@/components/ui/typography' -import { ConfigurationDialog } from '@/components/configuration-dialog' - -export function LayoutHeader() { - return ( - - - web3modal logo - - - - - - - GitHub - - - - Components - - - - Docs - - - - - - - - - ) -} diff --git a/apps/laboratory/src/components/Siwe/SiweData.tsx b/apps/laboratory/src/components/Siwe/SiweData.tsx index 5ec88bab35..57cfbf00cf 100644 --- a/apps/laboratory/src/components/Siwe/SiweData.tsx +++ b/apps/laboratory/src/components/Siwe/SiweData.tsx @@ -1,9 +1,16 @@ +import { + StackDivider, + Card, + CardHeader, + Heading, + CardBody, + Box, + Stack, + Text +} from '@chakra-ui/react' import { useEffect, useState } from 'react' import { useSession } from 'next-auth/react' import type { SIWESession } from '@web3modal/siwe' -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' -import { Column } from '@/components/ui/column' -import { Span } from '@/components/ui/typography' export function SiweData() { const [ready, setReady] = useState(false) @@ -15,27 +22,41 @@ export function SiweData() { }, []) return ready ? ( - - - SIWE Session Details + + + SIWE Session Details - - - - Session Status - {status} - - - Session Network - {`eip155:${session?.chainId}`} - - - Session Network Address - {session?.address || '-'} - - - + + } spacing="4"> + + + Session Status + + + {status} + + + + + + Session Network + + + {`eip155:${session?.chainId}`} + + + + + + Session Network Address + + + {session?.address} + + + + ) : null } diff --git a/apps/laboratory/src/components/Solana/SolanaSendTransactionTest.tsx b/apps/laboratory/src/components/Solana/SolanaSendTransactionTest.tsx index 225f5dc033..d0bff000d9 100644 --- a/apps/laboratory/src/components/Solana/SolanaSendTransactionTest.tsx +++ b/apps/laboratory/src/components/Solana/SolanaSendTransactionTest.tsx @@ -1,7 +1,6 @@ import { useState } from 'react' -import { Stack, Text, Spacer } from '@chakra-ui/react' +import { Button, useToast, Stack, Text, Spacer, Link } from '@chakra-ui/react' import { useWeb3ModalAccount, useWeb3ModalProvider } from '@web3modal/solana/react' -import { toast } from 'sonner' import { PublicKey, Transaction, @@ -12,15 +11,13 @@ import { } from '@solana/web3.js' import { solana } from '../../utils/ChainsUtil' -import { Button, buttonVariants } from '@/components/ui/button' -import { cn } from '@/lib/utils' -import Link from 'next/link' const PHANTOM_TESTNET_ADDRESS = 'EmT8r4E8ZjoQgt8sXGbaWBRMKfUXsVT1wonoSnJZ4nBn' const recipientAddress = new PublicKey(PHANTOM_TESTNET_ADDRESS) const amountInLamports = 100000000 export function SolanaSendTransactionTest() { + const toast = useToast() const { address, chainId } = useWeb3ModalAccount() const { walletProvider, connection } = useWeb3ModalProvider() const [loading, setLoading] = useState(false) @@ -56,10 +53,13 @@ export function SolanaSendTransactionTest() { transaction.recentBlockhash = blockhash const signature = await walletProvider.sendTransaction(transaction, connection as Connection) - toast.success('Success', { description: signature }) + toast({ title: 'Succcess', description: signature, status: 'success', isClosable: true }) } catch (err) { - toast.error('Success', { - description: (err as Error).message + toast({ + title: 'Error', + description: (err as Error).message, + status: 'error', + isClosable: true }) } finally { setLoading(false) @@ -106,10 +106,13 @@ export function SolanaSendTransactionTest() { transactionV0, connection as Connection ) - toast.success('Success', { description: signature }) + toast({ title: 'Succcess', description: signature, status: 'success', isClosable: true }) } catch (err) { - toast.error('Error', { - description: (err as Error).message + toast({ + title: 'Error', + description: (err as Error).message, + status: 'error', + isClosable: true }) } finally { setLoading(false) @@ -135,29 +138,25 @@ export function SolanaSendTransactionTest() { - {supportV0Transactions ? ( ) : null} - - Solana Faucet + + ) diff --git a/apps/laboratory/src/components/Solana/SolanaSignMessageTest.tsx b/apps/laboratory/src/components/Solana/SolanaSignMessageTest.tsx index d07f5c5474..fe74089b80 100644 --- a/apps/laboratory/src/components/Solana/SolanaSignMessageTest.tsx +++ b/apps/laboratory/src/components/Solana/SolanaSignMessageTest.tsx @@ -1,11 +1,11 @@ -import { toast } from 'sonner' +import { Button, useToast } from '@chakra-ui/react' import { useWeb3ModalAccount, useWeb3ModalProvider } from '@web3modal/solana/react' import { ConstantsUtil } from '../../utils/ConstantsUtil' -import { Button } from '@/components/ui/button' export function SolanaSignMessageTest() { + const toast = useToast() const { address } = useWeb3ModalAccount() const { walletProvider } = useWeb3ModalProvider() @@ -20,24 +20,33 @@ export function SolanaSignMessageTest() { // Backpack has specific signature format now if ((signature as { signature: Uint8Array }).signature) { - toast.success(ConstantsUtil.SigningSucceededToastTitle, { - description: (signature as { signature: Uint8Array }).signature + toast({ + title: ConstantsUtil.SigningSucceededToastTitle, + description: (signature as { signature: Uint8Array }).signature, + status: 'success', + isClosable: true }) return } - toast.success(ConstantsUtil.SigningSucceededToastTitle, { - description: signature as Uint8Array + toast({ + title: ConstantsUtil.SigningSucceededToastTitle, + description: signature as Uint8Array, + status: 'success', + isClosable: true }) } catch (err) { - toast.error(ConstantsUtil.SigningFailedToastTitle, { - description: 'Failed to sign message' + toast({ + title: ConstantsUtil.SigningFailedToastTitle, + description: 'Failed to sign message', + status: 'error', + isClosable: true }) } } return ( - ) diff --git a/apps/laboratory/src/components/Solana/SolanaSignTransactionTest.tsx b/apps/laboratory/src/components/Solana/SolanaSignTransactionTest.tsx index c03b936c0b..49cd7504f3 100644 --- a/apps/laboratory/src/components/Solana/SolanaSignTransactionTest.tsx +++ b/apps/laboratory/src/components/Solana/SolanaSignTransactionTest.tsx @@ -1,6 +1,5 @@ import { useState } from 'react' -import { Stack, Text, Spacer } from '@chakra-ui/react' -import { toast } from 'sonner' +import { Button, useToast, Stack, Text, Spacer, Link } from '@chakra-ui/react' import { PublicKey, Transaction, @@ -12,15 +11,13 @@ import { import { useWeb3ModalAccount, useWeb3ModalProvider } from '@web3modal/solana/react' import { solana } from '../../utils/ChainsUtil' -import { Button, buttonVariants } from '@/components/ui/button' -import Link from 'next/link' -import { cn } from '@/lib/utils' const PHANTOM_DEVNET_ADDRESS = 'EmT8r4E8ZjoQgt8sXGbaWBRMKfUXsVT1wonoSnJZ4nBn' const recipientAddress = new PublicKey(PHANTOM_DEVNET_ADDRESS) const amountInLamports = 100000000 export function SolanaSignTransactionTest() { + const toast = useToast() const { address, chainId } = useWeb3ModalAccount() const { walletProvider, connection } = useWeb3ModalProvider() const [loading, setLoading] = useState(false) @@ -51,10 +48,13 @@ export function SolanaSignTransactionTest() { const tx = await walletProvider.signTransaction(transaction) const signature = tx.signatures[0]?.signature - toast.success('Success', { description: signature }) + toast({ title: 'Succcess', description: signature, status: 'success', isClosable: true }) } catch (err) { - toast.success('Error', { - description: 'Failed to sign transaction' + toast({ + title: 'Error', + description: 'Failed to sign transaction', + status: 'error', + isClosable: true }) } finally { setLoading(false) @@ -93,10 +93,13 @@ export function SolanaSignTransactionTest() { const tx = await walletProvider.signTransaction(transactionV0) const signature = tx.signatures[0]?.signature - toast.success('Success', { description: signature }) + toast({ title: 'Succcess', description: signature, status: 'success', isClosable: true }) } catch (err) { - toast.success('Error', { - description: 'Failed to sign transaction' + toast({ + title: 'Error', + description: 'Failed to sign transaction', + status: 'error', + isClosable: true }) } finally { setLoading(false) @@ -122,8 +125,7 @@ export function SolanaSignTransactionTest() { @@ -131,20 +133,17 @@ export function SolanaSignTransactionTest() { ) : null} - - Solana Faucet + + ) diff --git a/apps/laboratory/src/components/Solana/SolanaTests.tsx b/apps/laboratory/src/components/Solana/SolanaTests.tsx index b68e8f8d89..8b4f94989b 100644 --- a/apps/laboratory/src/components/Solana/SolanaTests.tsx +++ b/apps/laboratory/src/components/Solana/SolanaTests.tsx @@ -1,42 +1,58 @@ import { useWeb3ModalAccount } from '@web3modal/solana/react' +import { + StackDivider, + Card, + CardHeader, + Heading, + CardBody, + Box, + Stack, + Text +} from '@chakra-ui/react' import { SolanaSignTransactionTest } from './SolanaSignTransactionTest' import { SolanaSendTransactionTest } from './SolanaSendTransactionTest' +import { SolanaSignMessageTest } from './SolanaSignMessageTest' import { solana } from '../../utils/ChainsUtil' -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' -import { Span } from '@/components/ui/typography' -import { Column } from '@/components/ui/column' - export function SolanaTests() { const { isConnected, currentChain } = useWeb3ModalAccount() return isConnected ? ( - - - Test Interactions + + + Test Interactions - - - - Sign Message - {currentChain?.chainId !== solana.chainId && ( - - Please ensure your wallet is connected to the {currentChain?.name} - - )} - - - Sign Transaction + + } spacing="4"> + + + Sign Message + + + + {currentChain?.chainId !== solana.chainId && ( + + + Please ensure your wallet is connected to the {currentChain?.name} + + + )} + + + Sign Transaction + - - - Sign and Send Transaction + + + + Sign and Send Transaction + - - - + + + ) : null } diff --git a/apps/laboratory/src/components/configuration-dialog/AccentColorInput.tsx b/apps/laboratory/src/components/Theming/AccentColorInput.tsx similarity index 100% rename from apps/laboratory/src/components/configuration-dialog/AccentColorInput.tsx rename to apps/laboratory/src/components/Theming/AccentColorInput.tsx diff --git a/apps/laboratory/src/components/Theming/BorderRadiusInput.tsx b/apps/laboratory/src/components/Theming/BorderRadiusInput.tsx new file mode 100644 index 0000000000..17f2e0a011 --- /dev/null +++ b/apps/laboratory/src/components/Theming/BorderRadiusInput.tsx @@ -0,0 +1,48 @@ +import { + Heading, + Slider, + SliderFilledTrack, + SliderMark, + SliderThumb, + SliderTrack +} from '@chakra-ui/react' + +import { ThemeStore } from '../../utils/StoreUtil' +import { useProxy } from 'valtio/utils' + +export default function BorderRadiusInput() { + const state = useProxy(ThemeStore.state) + + return ( + <> + + Border Radius + + { + ThemeStore.setBorderRadius(`${val}px`) + }} + > + + {state.borderRadius} + + + + + + + + ) +} diff --git a/apps/laboratory/src/components/configuration-dialog/MixColorInput.tsx b/apps/laboratory/src/components/Theming/MixColorInput.tsx similarity index 51% rename from apps/laboratory/src/components/configuration-dialog/MixColorInput.tsx rename to apps/laboratory/src/components/Theming/MixColorInput.tsx index afecbfe473..310062a295 100644 --- a/apps/laboratory/src/components/configuration-dialog/MixColorInput.tsx +++ b/apps/laboratory/src/components/Theming/MixColorInput.tsx @@ -1,13 +1,18 @@ -import { Grid, useRadioGroup } from '@chakra-ui/react' +import { + Grid, + Heading, + Slider, + SliderFilledTrack, + SliderMark, + SliderThumb, + SliderTrack, + useRadioGroup +} from '@chakra-ui/react' import { colors } from '../../utils/DataUtil' import RadioColor from './RadioColor' import { ThemeStore } from '../../utils/StoreUtil' import { useProxy } from 'valtio/utils' -import { Span } from '@/components/ui/typography' -import { Slider } from '@/components/ui/slider' -import { cn } from '@/lib/utils' -import { Row } from '@/components/ui/row' export default function MixColorInput() { const state = useProxy(ThemeStore.state) @@ -26,7 +31,9 @@ export default function MixColorInput() { return ( <> - Mix Color + + Mix Color + {colors.map(value => { const radio = getRadioProps({ value }) @@ -38,20 +45,31 @@ export default function MixColorInput() { ) })} - - - { - ThemeStore.setMixColorStrength(value[0] || 0) - }} - /> - {state.mixColorStrength}% - + { + ThemeStore.setMixColorStrength(val) + }} + > + + {state.mixColorStrength}% + + + + + + ) } diff --git a/apps/laboratory/src/components/configuration-dialog/RadioColor.tsx b/apps/laboratory/src/components/Theming/RadioColor.tsx similarity index 52% rename from apps/laboratory/src/components/configuration-dialog/RadioColor.tsx rename to apps/laboratory/src/components/Theming/RadioColor.tsx index 18e172425c..d2d24853cd 100644 --- a/apps/laboratory/src/components/configuration-dialog/RadioColor.tsx +++ b/apps/laboratory/src/components/Theming/RadioColor.tsx @@ -1,5 +1,4 @@ -import { Column } from '@/components/ui/column' -import { useRadio, type RadioProps } from '@chakra-ui/react' +import { Box, useRadio, type RadioProps } from '@chakra-ui/react' export default function RadioColor(props: RadioProps) { const { getInputProps, getRadioProps } = useRadio(props) @@ -10,13 +9,21 @@ export default function RadioColor(props: RadioProps) { const backgroundColor = typeof props.children === 'string' ? props.children : 'transparent' return ( - + - - + cursor="pointer" + borderRadius="full" + width="100%" + aspectRatio="1/1" + borderWidth="2px" + borderColor="transparent" + _checked={{ + borderColor: 'gray.900' + }} + > + ) } diff --git a/apps/laboratory/src/components/Wagmi/WagmiSendUSDCTest.tsx b/apps/laboratory/src/components/Wagmi/WagmiSendUSDCTest.tsx index c1ed26bd6c..58385bcb12 100644 --- a/apps/laboratory/src/components/Wagmi/WagmiSendUSDCTest.tsx +++ b/apps/laboratory/src/components/Wagmi/WagmiSendUSDCTest.tsx @@ -1,13 +1,7 @@ -import { toast } from 'sonner' +import { Button, useToast, Stack, Link, Text, Spacer, Input } from '@chakra-ui/react' import { useAccount, useWriteContract } from 'wagmi' import { useCallback, useState } from 'react' import { optimism, sepolia } from 'wagmi/chains' -import { Button, buttonVariants } from '@/components/ui/button' -import { Span } from '@/components/ui/typography' -import { Column } from '@/components/ui/column' -import { Input } from '@/components/ui/input' -import Link from 'next/link' -import { cn } from '@/lib/utils' const minTokenAbi = [ { @@ -41,19 +35,26 @@ export function WagmiSendUSDCTest() { const [address, setAddress] = useState('') const [amount, setAmount] = useState('') const { status, chain } = useAccount() + const toast = useToast() const { writeContract } = useWriteContract({ mutation: { onSuccess: hash => { setLoading(false) - toast.success('Transaction Success', { - description: hash + toast({ + title: 'Transaction Success', + description: hash, + status: 'success', + isClosable: true }) }, onError: () => { setLoading(false) - toast.error('Error', { - description: 'Failed to send transaction' + toast({ + title: 'Error', + description: 'Failed to send transaction', + status: 'error', + isClosable: true }) } } @@ -72,7 +73,8 @@ export function WagmiSendUSDCTest() { const allowedChains = [sepolia.id, optimism.id] as number[] return allowedChains.includes(Number(chain?.id)) && status === 'connected' ? ( - + + setAddress(e.target.value)} value={address} /> - - USDC Faucet + + - + ) : ( - + Switch to Sepolia or OP to test this feature - + ) } diff --git a/apps/laboratory/src/components/Wagmi/WagmiSignMessageTest.tsx b/apps/laboratory/src/components/Wagmi/WagmiSignMessageTest.tsx index 685476ce9e..5f1e81350a 100644 --- a/apps/laboratory/src/components/Wagmi/WagmiSignMessageTest.tsx +++ b/apps/laboratory/src/components/Wagmi/WagmiSignMessageTest.tsx @@ -1,9 +1,9 @@ +import { Button, useToast } from '@chakra-ui/react' import { useSignMessage, useAccount } from 'wagmi' import { ConstantsUtil } from '../../utils/ConstantsUtil' -import { Button } from '@/components/ui/button' -import { toast } from 'sonner' export function WagmiSignMessageTest() { + const toast = useToast() const { signMessageAsync } = useSignMessage() const { status } = useAccount() const isConnected = status === 'connected' @@ -11,23 +11,24 @@ export function WagmiSignMessageTest() { async function onSignMessage() { try { const signature = await signMessageAsync({ message: 'Hello Web3Modal!' }) - toast.success(ConstantsUtil.SigningSucceededToastTitle, { - description: signature + toast({ + title: ConstantsUtil.SigningSucceededToastTitle, + description: signature, + status: 'success', + isClosable: true }) } catch { - toast.error(ConstantsUtil.SigningFailedToastTitle, { - description: 'Failed to sign message' + toast({ + title: ConstantsUtil.SigningFailedToastTitle, + description: 'Failed to sign message', + status: 'error', + isClosable: true }) } } return ( - ) diff --git a/apps/laboratory/src/components/Wagmi/WagmiSignTypedDataTest.tsx b/apps/laboratory/src/components/Wagmi/WagmiSignTypedDataTest.tsx index bfdd788545..627a1e33a0 100644 --- a/apps/laboratory/src/components/Wagmi/WagmiSignTypedDataTest.tsx +++ b/apps/laboratory/src/components/Wagmi/WagmiSignTypedDataTest.tsx @@ -1,5 +1,4 @@ -import { Button } from '@/components/ui/button' -import { toast } from 'sonner' +import { Button, useToast } from '@chakra-ui/react' import { useAccount, useSignTypedData } from 'wagmi' // Example data @@ -28,6 +27,7 @@ const message = { } as const export function WagmiSignTypedDataTest() { + const toast = useToast() const { chain, status } = useAccount() const domain = { name: 'Ether Mail', @@ -47,10 +47,13 @@ export function WagmiSignTypedDataTest() { primaryType: 'Mail', types }) - toast.success('Success', { description: signature }) + toast({ title: 'Success', description: signature, status: 'success', isClosable: true }) } catch { - toast.error('Error', { - description: 'Failed to sign message' + toast({ + title: 'Error', + description: 'Failed to sign message', + status: 'error', + isClosable: true }) } } @@ -59,8 +62,7 @@ export function WagmiSignTypedDataTest() { diff --git a/apps/laboratory/src/components/Wagmi/WagmiTests.tsx b/apps/laboratory/src/components/Wagmi/WagmiTests.tsx index 47a3a127d3..5c8ddfce85 100644 --- a/apps/laboratory/src/components/Wagmi/WagmiTests.tsx +++ b/apps/laboratory/src/components/Wagmi/WagmiTests.tsx @@ -1,43 +1,53 @@ import { WagmiTransactionTest } from './WagmiTransactionTest' import { WagmiSignMessageTest } from './WagmiSignMessageTest' import { WagmiSignTypedDataTest } from './WagmiSignTypedDataTest' +import { StackDivider, Card, CardHeader, Heading, CardBody, Box, Stack } from '@chakra-ui/react' import { WagmiWriteContractTest } from './WagmiWriteContractTest' import { WagmiSendUSDCTest } from './WagmiSendUSDCTest' -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' -import { Span } from '@/components/ui/typography' -import { Column } from '@/components/ui/column' export function WagmiTests() { return ( - - - Test Interactions + + + Test Interactions - - - - Sign Message + + + } spacing="4"> + + + Sign Message + - - - Sign Typed Data + + + + + Sign Typed Data + - + - - Sign Transaction + + + Sign Transaction + - - - Contract Write + + + + Contract Write + - - - USDC Send + + + + USDC Send + - - - + + + ) } diff --git a/apps/laboratory/src/components/Wagmi/WagmiTransactionTest.tsx b/apps/laboratory/src/components/Wagmi/WagmiTransactionTest.tsx index fc0e97b33e..9085b4e327 100644 --- a/apps/laboratory/src/components/Wagmi/WagmiTransactionTest.tsx +++ b/apps/laboratory/src/components/Wagmi/WagmiTransactionTest.tsx @@ -1,15 +1,9 @@ -import { toast } from 'sonner' +import { Button, useToast, Stack, Link, Text, Spacer } from '@chakra-ui/react' import { parseGwei, type Address } from 'viem' import { useEstimateGas, useSendTransaction, useAccount } from 'wagmi' import { vitalikEthAddress } from '../../utils/DataUtil' import { useCallback, useState } from 'react' import { optimism, optimismSepolia, sepolia } from 'wagmi/chains' -import { Span } from '@/components/ui/typography' -import { Row } from '@/components/ui/row' -import { cn } from '@/lib/utils' -import { Button, buttonVariants } from '@/components/ui/button' -import { Column } from '@/components/ui/column' -import Link from 'next/link' const TEST_TX = { to: vitalikEthAddress as Address, @@ -17,23 +11,29 @@ const TEST_TX = { } export function WagmiTransactionTest() { + const toast = useToast() const { status, chain } = useAccount() const { data: gas, error: prepareError } = useEstimateGas(TEST_TX) const [isLoading, setLoading] = useState(false) const isConnected = status === 'connected' - const { sendTransaction } = useSendTransaction({ mutation: { onSuccess: hash => { setLoading(false) - toast.success('Transaction Success', { - description: hash + toast({ + title: 'Transaction Success', + description: hash, + status: 'success', + isClosable: true }) }, onError: () => { setLoading(false) - toast.error('Error', { - description: 'Failed to send transaction' + toast({ + title: 'Error', + description: 'Failed to send transaction', + status: 'error', + isClosable: true }) } } @@ -41,8 +41,11 @@ export function WagmiTransactionTest() { const onSendTransaction = useCallback(() => { if (prepareError) { - toast.error('Error', { - description: 'Not enough funds for transaction' + toast({ + title: 'Error', + description: 'Not enough funds for transaction', + status: 'error', + isClosable: true }) } else { setLoading(true) @@ -56,36 +59,33 @@ export function WagmiTransactionTest() { const allowedChains = [sepolia.id, optimism.id, optimismSepolia.id] as number[] return allowedChains.includes(Number(chain?.id)) && status === 'connected' ? ( - + - - + + + + + + + + + + ) : ( - + Switch to Sepolia or OP to test this feature - + ) } diff --git a/apps/laboratory/src/components/Wagmi/WagmiWriteContractTest.tsx b/apps/laboratory/src/components/Wagmi/WagmiWriteContractTest.tsx index 118ec7ec09..54bd0af3b8 100644 --- a/apps/laboratory/src/components/Wagmi/WagmiWriteContractTest.tsx +++ b/apps/laboratory/src/components/Wagmi/WagmiWriteContractTest.tsx @@ -1,17 +1,12 @@ -import { toast } from 'sonner' +import { Button, useToast, Stack, Link, Text, Spacer, Flex } from '@chakra-ui/react' import { parseEther } from 'viem' import { useAccount, useSimulateContract, useWriteContract, useReadContract } from 'wagmi' import { useCallback, useEffect } from 'react' import { optimism, sepolia } from 'wagmi/chains' import { abi, address } from '../../utils/DonutContract' -import { Span } from '@/components/ui/typography' -import { Column } from '@/components/ui/column' -import { Button, buttonVariants } from '@/components/ui/button' -import { Row } from '@/components/ui/row' -import Link from 'next/link' -import { cn } from '@/lib/utils' export function WagmiWriteContractTest() { + const toast = useToast() const { status, chain, address: accountAddress } = useAccount() const { data: donutsOwned, @@ -36,8 +31,11 @@ export function WagmiWriteContractTest() { const onSendTransaction = useCallback(async () => { if (simulateError || !simulateData?.request) { - toast.error('Error', { - description: 'Not able to execute this transaction. Check your balance.' + toast({ + title: 'Error', + description: 'Not able to execute this transaction. Check your balance.', + status: 'error', + isClosable: true }) } else { writeContract(simulateData?.request) @@ -47,12 +45,18 @@ export function WagmiWriteContractTest() { useEffect(() => { if (data) { - toast.success('Donut Purchase Success!', { - description: data + toast({ + title: 'Donut Purchase Success!', + description: data, + status: 'success', + isClosable: true }) } else if (error) { - toast.success('Error', { - description: 'Failed to purchase donut' + toast({ + title: 'Error', + description: 'Failed to purchase donut', + status: 'error', + isClosable: true }) } reset() @@ -61,44 +65,40 @@ export function WagmiWriteContractTest() { const allowedChains = [sepolia.id, optimism.id] as number[] return allowedChains.includes(Number(chain?.id)) && status === 'connected' ? ( - - - - {donutsQueryLoading || donutsQueryRefetching ? ( - Fetching donuts... - ) : ( - Crypto donuts left: {donutsOwned?.toString()} - )} - + + + {donutsQueryLoading || donutsQueryRefetching ? ( + Fetching donuts... + ) : ( + + Crypto donuts left: + {donutsOwned?.toString()} + + )} + - - + + + - + + + + ) : ( - + Switch to Sepolia or OP to test this feature - + ) } diff --git a/apps/laboratory/src/components/Web3ModalButtons.tsx b/apps/laboratory/src/components/Web3ModalButtons.tsx index 098c8d8e89..1b633ee34e 100644 --- a/apps/laboratory/src/components/Web3ModalButtons.tsx +++ b/apps/laboratory/src/components/Web3ModalButtons.tsx @@ -1,29 +1,35 @@ -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' -import { Column } from '@/components/ui/column' -import { Span } from '@/components/ui/typography' +import { Stack, Card, CardHeader, Heading, CardBody, Box, StackDivider } from '@chakra-ui/react' export function Web3ModalButtons() { return ( - - - Web3Modal Interactions + + + Web3Modal Interactions - - - - Connect / Account Button + + + } spacing="4"> + + + Connect / Account Button + - - - Network Button + + + + + Network Button + - - - Onramp Widget + + + + Onramp Widget + - - - + + + ) } diff --git a/apps/laboratory/src/components/configuration-dialog/BorderRadiusInput.tsx b/apps/laboratory/src/components/configuration-dialog/BorderRadiusInput.tsx deleted file mode 100644 index 9aa0fc88d2..0000000000 --- a/apps/laboratory/src/components/configuration-dialog/BorderRadiusInput.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { ThemeStore } from '../../utils/StoreUtil' -import { useProxy } from 'valtio/utils' -import { cn } from '@/lib/utils' -import { Span } from '@/components/ui/typography' -import { Row } from '@/components/ui/row' -import { Slider } from '@/components/ui/slider' - -export default function BorderRadiusInput() { - const state = useProxy(ThemeStore.state) - - return ( - <> - Border Radius - - { - ThemeStore.setBorderRadius(`${value[0] || 0}px`) - }} - /> - {state.borderRadius} - - - ) -} diff --git a/apps/laboratory/src/components/configuration-dialog/index.tsx b/apps/laboratory/src/components/configuration-dialog/index.tsx deleted file mode 100644 index 8a6812ef97..0000000000 --- a/apps/laboratory/src/components/configuration-dialog/index.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import * as React from 'react' - -import { Column } from '@/components/ui/column' -import { Dialog, DialogContent } from '@/components/ui/dialog' -import { cn } from '@/lib/utils' -import { ThemeStore } from '@/utils/StoreUtil' -import { GearIcon } from '@radix-ui/react-icons' -import { DialogTrigger } from '@radix-ui/react-dialog' -import { buttonVariants } from '@/components/ui/button' -import { Span } from '@/components/ui/typography' -import { useTheme } from 'next-themes' - -import MixColorInput from './MixColorInput' -import AccentColorInput from './AccentColorInput' -import BorderRadiusInput from './BorderRadiusInput' - -export function ConfigurationDialog() { - const { theme } = useTheme() - - React.useEffect(() => { - if (ThemeStore.state.modal) { - ThemeStore.state.modal.setThemeMode(theme) - } - }, [theme]) - - return ( - - - - Configure - - - - - - - - - - - - ) -} diff --git a/apps/laboratory/src/components/icons/ethers.tsx b/apps/laboratory/src/components/icons/ethers.tsx deleted file mode 100644 index f7718f4692..0000000000 --- a/apps/laboratory/src/components/icons/ethers.tsx +++ /dev/null @@ -1,12 +0,0 @@ -export default function EthersIcon(props: React.SVGProps) { - return ( - - - - ) -} diff --git a/apps/laboratory/src/components/icons/solana.tsx b/apps/laboratory/src/components/icons/solana.tsx deleted file mode 100644 index 61b66274cf..0000000000 --- a/apps/laboratory/src/components/icons/solana.tsx +++ /dev/null @@ -1,18 +0,0 @@ -export default function SolanaIcon(props: React.SVGProps) { - return ( - - - - - - ) -} diff --git a/apps/laboratory/src/components/icons/wagmi.tsx b/apps/laboratory/src/components/icons/wagmi.tsx deleted file mode 100644 index f00cbe8d77..0000000000 --- a/apps/laboratory/src/components/icons/wagmi.tsx +++ /dev/null @@ -1,19 +0,0 @@ -export default function WagmiIcon(props: React.SVGProps) { - return ( - - - - - - - - - - - ) -} diff --git a/apps/laboratory/src/components/theme-provider.tsx b/apps/laboratory/src/components/theme-provider.tsx deleted file mode 100644 index 06159b434e..0000000000 --- a/apps/laboratory/src/components/theme-provider.tsx +++ /dev/null @@ -1,10 +0,0 @@ -'use client' - -import * as React from 'react' - -import { ThemeProvider as NextThemesProvider } from 'next-themes' -import { type ThemeProviderProps } from 'next-themes/dist/types' - -export function ThemeProvider({ children, ...props }: ThemeProviderProps) { - return {children} -} diff --git a/apps/laboratory/src/components/theme-toggle.tsx b/apps/laboratory/src/components/theme-toggle.tsx deleted file mode 100644 index 0b2f0c822b..0000000000 --- a/apps/laboratory/src/components/theme-toggle.tsx +++ /dev/null @@ -1,40 +0,0 @@ -'use client' - -import * as React from 'react' - -import { MoonIcon, SunIcon } from '@radix-ui/react-icons' -import { useTheme } from 'next-themes' - -import { Button } from '@/components/ui/button' -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger -} from '@/components/ui/dropdown-menu' -import { cn } from '@/lib/utils' - -type ModeToggleProps = { - className?: string -} - -export function ModeToggle({ className }: ModeToggleProps) { - const { setTheme } = useTheme() - - return ( - - - - - - setTheme('light')}>Light - setTheme('dark')}>Dark - setTheme('system')}>System - - - ) -} diff --git a/apps/laboratory/src/components/ui/button.tsx b/apps/laboratory/src/components/ui/button.tsx deleted file mode 100644 index 569d9ddba0..0000000000 --- a/apps/laboratory/src/components/ui/button.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import * as React from 'react' -import { Slot } from '@radix-ui/react-slot' -import { cva, type VariantProps } from 'class-variance-authority' - -import { cn } from '@/lib/utils' - -const buttonVariants = cva( - 'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50', - { - variants: { - variant: { - default: 'bg-primary text-primary-foreground hover:bg-primary/90', - destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90', - outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground', - secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80', - ghost: 'hover:bg-accent hover:text-accent-foreground', - link: 'text-primary underline-offset-4 hover:underline' - }, - size: { - default: 'h-10 px-4 py-2', - sm: 'h-9 rounded-md px-3', - lg: 'h-11 rounded-md px-8', - icon: 'h-10 w-10' - } - }, - defaultVariants: { - variant: 'default', - size: 'default' - } - } -) - -export interface ButtonProps - extends React.ButtonHTMLAttributes, - VariantProps { - asChild?: boolean -} - -const Button = React.forwardRef( - ({ className, variant, size, asChild = false, ...props }, ref) => { - const Comp = asChild ? Slot : 'button' - - return ( - - ) - } -) -Button.displayName = 'Button' - -export { Button, buttonVariants } diff --git a/apps/laboratory/src/components/ui/card.tsx b/apps/laboratory/src/components/ui/card.tsx deleted file mode 100644 index a0147668d0..0000000000 --- a/apps/laboratory/src/components/ui/card.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import * as React from 'react' - -import { cn } from '@/lib/utils' - -const Card = React.forwardRef>( - ({ className, ...props }, ref) => ( -
    - ) -) -Card.displayName = 'Card' - -const CardHeader = React.forwardRef>( - ({ className, ...props }, ref) => ( -
    - ) -) -CardHeader.displayName = 'CardHeader' - -const CardTitle = React.forwardRef>( - ({ className, ...props }, ref) => ( -

    - ) -) -CardTitle.displayName = 'CardTitle' - -const CardDescription = React.forwardRef< - HTMLParagraphElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -

    -)) -CardDescription.displayName = 'CardDescription' - -const CardContent = React.forwardRef>( - ({ className, ...props }, ref) => ( -

    - ) -) -CardContent.displayName = 'CardContent' - -const CardFooter = React.forwardRef>( - ({ className, ...props }, ref) => ( -
    - ) -) -CardFooter.displayName = 'CardFooter' - -export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } diff --git a/apps/laboratory/src/components/ui/column.tsx b/apps/laboratory/src/components/ui/column.tsx deleted file mode 100644 index 550dbbcc86..0000000000 --- a/apps/laboratory/src/components/ui/column.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import * as React from 'react' - -import { cn } from '@/lib/utils' - -const Column = React.forwardRef>( - ({ className, ...props }, ref) => { - return
    - } -) -Column.displayName = 'Column' - -export { Column } diff --git a/apps/laboratory/src/components/ui/dialog.tsx b/apps/laboratory/src/components/ui/dialog.tsx deleted file mode 100644 index 796aa05ac7..0000000000 --- a/apps/laboratory/src/components/ui/dialog.tsx +++ /dev/null @@ -1,114 +0,0 @@ -'use client' - -import * as React from 'react' -import * as DialogPrimitive from '@radix-ui/react-dialog' - -import { cn } from '@/lib/utils' -import { Cross1Icon } from '@radix-ui/react-icons' - -const Dialog = DialogPrimitive.Root - -const DialogTrigger = DialogPrimitive.Trigger - -const DialogPortal = DialogPrimitive.Portal - -const DialogClose = DialogPrimitive.Close - -const DialogOverlay = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) - -DialogOverlay.displayName = DialogPrimitive.Overlay.displayName - -const DialogContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, children, ...props }, ref) => ( - - - - {children} - - - Close - - - -)) -DialogContent.displayName = DialogPrimitive.Content.displayName - -function DialogHeader({ className, ...props }: React.HTMLAttributes) { - return ( -
    - ) -} -DialogHeader.displayName = 'DialogHeader' - -function DialogFooter({ className, ...props }: React.HTMLAttributes) { - return ( -
    - ) -} -DialogFooter.displayName = 'DialogFooter' - -const DialogTitle = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -DialogTitle.displayName = DialogPrimitive.Title.displayName - -const DialogDescription = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -DialogDescription.displayName = DialogPrimitive.Description.displayName - -export { - Dialog, - DialogPortal, - DialogOverlay, - DialogClose, - DialogTrigger, - DialogContent, - DialogHeader, - DialogFooter, - DialogTitle, - DialogDescription -} diff --git a/apps/laboratory/src/components/ui/dropdown-menu.tsx b/apps/laboratory/src/components/ui/dropdown-menu.tsx deleted file mode 100644 index 6f94d003ef..0000000000 --- a/apps/laboratory/src/components/ui/dropdown-menu.tsx +++ /dev/null @@ -1,196 +0,0 @@ -'use client' - -import * as React from 'react' -import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu' - -import { cn } from '@/lib/utils' -import { CheckIcon, ChevronRightIcon, CircleIcon } from '@radix-ui/react-icons' - -const DropdownMenu = DropdownMenuPrimitive.Root - -const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger - -const DropdownMenuGroup = DropdownMenuPrimitive.Group - -const DropdownMenuPortal = DropdownMenuPrimitive.Portal - -const DropdownMenuSub = DropdownMenuPrimitive.Sub - -const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup - -const DropdownMenuSubTrigger = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & { - inset?: boolean - } ->(({ className, inset, children, ...props }, ref) => ( - - {children} - - -)) -DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName - -const DropdownMenuSubContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName - -const DropdownMenuContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, sideOffset = 4, ...props }, ref) => ( - - - -)) -DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName - -const DropdownMenuItem = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & { - inset?: boolean - } ->(({ className, inset, ...props }, ref) => ( - -)) -DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName - -const DropdownMenuCheckboxItem = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, children, checked, ...props }, ref) => ( - - - - - - - {children} - -)) -DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName - -const DropdownMenuRadioItem = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, children, ...props }, ref) => ( - - - - - - - {children} - -)) -DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName - -const DropdownMenuLabel = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & { - inset?: boolean - } ->(({ className, inset, ...props }, ref) => ( - -)) -DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName - -const DropdownMenuSeparator = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName - -interface DropdownMenuShortcutProps extends React.HTMLAttributes { - className?: string -} -function DropdownMenuShortcut({ className, ...props }: DropdownMenuShortcutProps) { - return -} -DropdownMenuShortcut.displayName = 'DropdownMenuShortcut' - -export { - DropdownMenu, - DropdownMenuTrigger, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuCheckboxItem, - DropdownMenuRadioItem, - DropdownMenuLabel, - DropdownMenuSeparator, - DropdownMenuShortcut, - DropdownMenuGroup, - DropdownMenuPortal, - DropdownMenuSub, - DropdownMenuSubContent, - DropdownMenuSubTrigger, - DropdownMenuRadioGroup -} diff --git a/apps/laboratory/src/components/ui/input.tsx b/apps/laboratory/src/components/ui/input.tsx deleted file mode 100644 index bee17ea3e7..0000000000 --- a/apps/laboratory/src/components/ui/input.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import * as React from 'react' - -import { cn } from '@/lib/utils' - -const Input = React.forwardRef>( - ({ className, type, ...props }, ref) => { - return ( - - ) - } -) -Input.displayName = 'Input' - -export { Input } diff --git a/apps/laboratory/src/components/ui/row.tsx b/apps/laboratory/src/components/ui/row.tsx deleted file mode 100644 index 1ba7af43c1..0000000000 --- a/apps/laboratory/src/components/ui/row.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import * as React from 'react' - -import { cn } from '@/lib/utils' - -const Row = React.forwardRef>( - ({ className, ...props }, ref) => { - return
    - } -) -Row.displayName = 'Row' - -export { Row } diff --git a/apps/laboratory/src/components/ui/slider.tsx b/apps/laboratory/src/components/ui/slider.tsx deleted file mode 100644 index 4622e78859..0000000000 --- a/apps/laboratory/src/components/ui/slider.tsx +++ /dev/null @@ -1,25 +0,0 @@ -'use client' - -import * as React from 'react' -import * as SliderPrimitive from '@radix-ui/react-slider' - -import { cn } from '@/lib/utils' - -const Slider = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - - - - - - -)) -Slider.displayName = SliderPrimitive.Root.displayName - -export { Slider } diff --git a/apps/laboratory/src/components/ui/typography.tsx b/apps/laboratory/src/components/ui/typography.tsx deleted file mode 100644 index 40c81bb9b8..0000000000 --- a/apps/laboratory/src/components/ui/typography.tsx +++ /dev/null @@ -1,104 +0,0 @@ -import * as React from 'react' - -import { cn } from '@/lib/utils' - -type ElementProps = { - children?: React.ReactNode - className?: string -} - -export function H1({ children, ...props }: ElementProps) { - return ( -

    - {children} -

    - ) -} -export function H2({ children, ...props }: ElementProps) { - return ( -

    - {children} -

    - ) -} -export function H3({ children, ...props }: ElementProps) { - return ( -

    - {children} -

    - ) -} -export function H4({ children, ...props }: ElementProps) { - return ( -

    - {children} -

    - ) -} - -export function P({ children, ...props }: ElementProps) { - return ( -

    - {children} -

    - ) -} -export function Span({ children, ...props }: ElementProps) { - return ( - - {children} - - ) -} - -export function Blockquote({ children, ...props }: ElementProps) { - return ( -
    - {children} -
    - ) -} -export function List({ children, ...props }: ElementProps) { - return ( -
      li]:mt-2', props.className)} {...props}> - {children} -
    - ) -} -export function ListItem({ children, ...props }: ElementProps) { - return
  • {children}
  • -} -export function InlineCode({ children, ...props }: ElementProps) { - return ( - - {children} - - ) -} -export function Lead({ children, ...props }: ElementProps) { - return

    {children}

    -} diff --git a/apps/laboratory/src/layout/LayoutHeader.tsx b/apps/laboratory/src/layout/LayoutHeader.tsx new file mode 100644 index 0000000000..c26788c0d2 --- /dev/null +++ b/apps/laboratory/src/layout/LayoutHeader.tsx @@ -0,0 +1,46 @@ +import { + Image, + Stack, + HStack, + Button, + Spacer, + Link as CLink, + useDisclosure +} from '@chakra-ui/react' +import Link from 'next/link' +import { IoSettingsOutline } from 'react-icons/io5' +import { OptionsDrawer } from './OptionsDrawer' + +export function LayoutHeader() { + const controls = useDisclosure() + + return ( + <> + + + + + + + + + + GitHub + + + Gallery + + + Docs + + + + + + + + + ) +} diff --git a/apps/laboratory/src/layout/OptionsDrawer.tsx b/apps/laboratory/src/layout/OptionsDrawer.tsx new file mode 100644 index 0000000000..86807bcd1a --- /dev/null +++ b/apps/laboratory/src/layout/OptionsDrawer.tsx @@ -0,0 +1,86 @@ +import { + Drawer, + DrawerBody, + DrawerCloseButton, + DrawerContent, + DrawerHeader, + DrawerOverlay, + Flex, + Icon, + useColorMode, + useDisclosure +} from '@chakra-ui/react' +import { BsFillMoonFill, BsFillSunFill } from 'react-icons/bs' +import AccentColorInput from '../components/Theming/AccentColorInput' +import BorderRadiusInput from '../components/Theming/BorderRadiusInput' +import MixColorInput from '../components/Theming/MixColorInput' +import { useEffect } from 'react' +import { ThemeStore } from '../utils/StoreUtil' + +interface Props { + controls: ReturnType +} + +export function OptionsDrawer({ controls }: Props) { + const { colorMode, toggleColorMode } = useColorMode() + const { isOpen, onClose } = controls + + useEffect(() => { + if (ThemeStore.state.modal) { + ThemeStore.state.modal.setThemeMode(colorMode) + } + }, [colorMode]) + + return ( + + + + + Edit Theme + + {colorMode === 'light' ? ( + + + + ) : ( + + + + )} + + + + + + + + + + + + + + + + ) +} diff --git a/apps/laboratory/src/layout/index.tsx b/apps/laboratory/src/layout/index.tsx new file mode 100644 index 0000000000..12fe6c2ad8 --- /dev/null +++ b/apps/laboratory/src/layout/index.tsx @@ -0,0 +1,16 @@ +import type { ReactNode } from 'react' +import { LayoutHeader } from './LayoutHeader' +import { Container } from '@chakra-ui/react' + +type Props = { + children: ReactNode | ReactNode[] +} + +export default function Layout({ children }: Props) { + return ( + + +
    {children}
    +
    + ) +} diff --git a/apps/laboratory/src/lib/utils.ts b/apps/laboratory/src/lib/utils.ts deleted file mode 100644 index d32b0fe652..0000000000 --- a/apps/laboratory/src/lib/utils.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { type ClassValue, clsx } from 'clsx' -import { twMerge } from 'tailwind-merge' - -export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)) -} diff --git a/apps/laboratory/src/pages/_app.tsx b/apps/laboratory/src/pages/_app.tsx index 49a8d2b41d..8e8805c984 100644 --- a/apps/laboratory/src/pages/_app.tsx +++ b/apps/laboratory/src/pages/_app.tsx @@ -1,18 +1,9 @@ +import { ChakraProvider } from '@chakra-ui/react' import type { AppProps } from 'next/app' +import Layout from '../layout' import { bootstrapSentry } from '../utils/SentryUtil' import { SessionProvider } from 'next-auth/react' import type { Session } from 'next-auth' -import { Toaster } from 'sonner' - -import { Inter } from 'next/font/google' - -import '../../globals.css' -import { cn } from '@/lib/utils' -import { ThemeProvider } from '@/components/theme-provider' -import { LayoutHeader } from '@/components/Header' - -// eslint-disable-next-line new-cap -const inter = Inter({ subsets: ['latin'] }) bootstrapSentry() @@ -23,16 +14,12 @@ export default function App({ session: Session }>) { return ( - - -
    -
    - - - -
    -
    -
    -
    + + + + + + + ) } diff --git a/apps/laboratory/src/pages/_document.tsx b/apps/laboratory/src/pages/_document.tsx index 5e51ebfc7f..eff2ba5931 100644 --- a/apps/laboratory/src/pages/_document.tsx +++ b/apps/laboratory/src/pages/_document.tsx @@ -1,11 +1,12 @@ import { Html, Head, Main, NextScript } from 'next/document' -import { cn } from '@/lib/utils' +import { ColorModeScript } from '@chakra-ui/react' export default function Document() { return ( - + +
    diff --git a/apps/laboratory/src/pages/index.tsx b/apps/laboratory/src/pages/index.tsx index e7e84546ba..8965d1e17d 100644 --- a/apps/laboratory/src/pages/index.tsx +++ b/apps/laboratory/src/pages/index.tsx @@ -1,95 +1,104 @@ +import { + Heading, + Card, + CardHeader, + CardBody, + Stack, + StackDivider, + Box, + Text, + Button, + Link +} from '@chakra-ui/react' +import { IoArrowForward } from 'react-icons/io5' import { wagmiSdkOptions, ethersSdkOptions, solanaSdkOptions } from '../utils/DataUtil' -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' -import { Column } from '@/components/ui/column' -import { Row } from '@/components/ui/row' -import { P, Span } from '@/components/ui/typography' -import Link from 'next/link' -import { Button } from '@/components/ui/button' -import { ArrowRightIcon } from '@radix-ui/react-icons' -import WagmiIcon from '@/components/icons/wagmi' -import EthersIcon from '@/components/icons/ethers' -import SolanaIcon from '@/components/icons/solana' export default function HomePage() { return ( - - - - - Wagmi + <> + + + Wagmi - - + + + } spacing="4"> {wagmiSdkOptions.map(option => ( - - - - {option.title} -

    {option.description}

    -
    + + + + + {option.title} + + + {option.description} + + - + -
    -
    +
    + ))} -
    -
    + +
    - - - - Ethers + + + Ethers - - + + + } spacing="4"> {ethersSdkOptions.map(option => ( - - - - {option.title} -

    {option.description}

    -
    + + + + + {option.title} + + + {option.description} + + - + -
    -
    +
    + ))} -
    -
    + +
    - - - - Solana + + + Solana - - + + + } spacing="4"> {solanaSdkOptions.map(option => ( - - - - {option.title} -

    {option.description}

    -
    + + + + + {option.title} + + + {option.description} + + - + -
    -
    +
    + ))} -
    -
    + +
    -
    + ) } diff --git a/apps/laboratory/tailwind.config.js b/apps/laboratory/tailwind.config.js deleted file mode 100644 index 262ca98d99..0000000000 --- a/apps/laboratory/tailwind.config.js +++ /dev/null @@ -1,72 +0,0 @@ -/** @type {import('tailwindcss').Config} */ -module.exports = { - darkMode: ['class'], - content: ['./src/**/*.{ts,tsx}'], - prefix: '', - theme: { - container: { - center: true, - padding: '2rem', - screens: { - '2xl': '1400px' - } - }, - extend: { - colors: { - border: 'hsl(var(--border))', - input: 'hsl(var(--input))', - ring: 'hsl(var(--ring))', - background: 'hsl(var(--background))', - foreground: 'hsl(var(--foreground))', - primary: { - DEFAULT: 'hsl(var(--primary))', - foreground: 'hsl(var(--primary-foreground))' - }, - secondary: { - DEFAULT: 'hsl(var(--secondary))', - foreground: 'hsl(var(--secondary-foreground))' - }, - destructive: { - DEFAULT: 'hsl(var(--destructive))', - foreground: 'hsl(var(--destructive-foreground))' - }, - muted: { - DEFAULT: 'hsl(var(--muted))', - foreground: 'hsl(var(--muted-foreground))' - }, - accent: { - DEFAULT: 'hsl(var(--accent))', - foreground: 'hsl(var(--accent-foreground))' - }, - popover: { - DEFAULT: 'hsl(var(--popover))', - foreground: 'hsl(var(--popover-foreground))' - }, - card: { - DEFAULT: 'hsl(var(--card))', - foreground: 'hsl(var(--card-foreground))' - } - }, - borderRadius: { - lg: 'var(--radius)', - md: 'calc(var(--radius) - 2px)', - sm: 'calc(var(--radius) - 4px)' - }, - keyframes: { - 'accordion-down': { - from: { height: '0' }, - to: { height: 'var(--radix-accordion-content-height)' } - }, - 'accordion-up': { - from: { height: 'var(--radix-accordion-content-height)' }, - to: { height: '0' } - } - }, - animation: { - 'accordion-down': 'accordion-down 0.2s ease-out', - 'accordion-up': 'accordion-up 0.2s ease-out' - } - } - }, - plugins: [require('tailwindcss-animate')] -} diff --git a/apps/laboratory/tsconfig.json b/apps/laboratory/tsconfig.json index 479b60202d..75754d6e68 100644 --- a/apps/laboratory/tsconfig.json +++ b/apps/laboratory/tsconfig.json @@ -2,10 +2,7 @@ "extends": "../../tsconfig.json", "compilerOptions": { "jsx": "preserve", - "noEmit": true, - "paths": { - "@/*": ["./src/*"] - } + "noEmit": true }, "include": ["next-env.d.ts", "src", "tests", "playwright.config.ts"] } diff --git a/package-lock.json b/package-lock.json index 09e3098814..da18aa2115 100644 --- a/package-lock.json +++ b/package-lock.json @@ -161,11 +161,6 @@ "@chakra-ui/react": "2.8.2", "@emotion/react": "11.11.3", "@emotion/styled": "11.11.0", - "@radix-ui/react-dialog": "1.0.5", - "@radix-ui/react-dropdown-menu": "2.0.6", - "@radix-ui/react-icons": "1.3.0", - "@radix-ui/react-slider": "1.1.2", - "@radix-ui/react-slot": "1.0.2", "@sentry/browser": "7.92.0", "@sentry/react": "7.92.0", "@solana/web3.js": "1.87.6", @@ -174,16 +169,12 @@ "@web3modal/siwe": "4.1.3-5f2ae345.1", "@web3modal/solana": "4.1.3-5f2ae345.1", "@web3modal/wagmi": "4.1.3-5f2ae345.1", - "class-variance-authority": "0.7.0", "ethers": "6.9.0", "framer-motion": "10.17.9", "next": "14.0.4", "next-auth": "4.24.5", - "next-themes": "0.3.0", "react-icons": "4.12.0", "siwe": "2.1.4", - "sonner": "1.4.41", - "tailwindcss-animate": "1.0.7", "valtio": "1.11.2", "viem": "2.9.3", "wagmi": "2.5.7" @@ -192,49 +183,7 @@ "@aws-sdk/client-cloudwatch": "3.509.0", "@mailsac/api": "1.0.5", "@playwright/test": "1.40.1", - "autoprefixer": "10.4.19", - "clsx": "2.1.0", - "dotenv": "16.3.1", - "postcss": "8.4.38", - "tailwind-merge": "2.2.2", - "tailwindcss": "3.4.3" - } - }, - "apps/laboratory/node_modules/autoprefixer": { - "version": "10.4.19", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz", - "integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "browserslist": "^4.23.0", - "caniuse-lite": "^1.0.30001599", - "fraction.js": "^4.3.7", - "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", - "postcss-value-parser": "^4.2.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.1.0" + "dotenv": "16.3.1" } }, "apps/laboratory/node_modules/framer-motion": { @@ -306,7 +255,7 @@ } } }, - "apps/laboratory/node_modules/next/node_modules/postcss": { + "apps/laboratory/node_modules/postcss": { "version": "8.4.31", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", @@ -333,102 +282,6 @@ "node": "^10 || ^12 || >=14" } }, - "apps/laboratory/node_modules/object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "apps/laboratory/node_modules/postcss": { - "version": "8.4.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "apps/laboratory/node_modules/sonner": { - "version": "1.4.41", - "resolved": "https://registry.npmjs.org/sonner/-/sonner-1.4.41.tgz", - "integrity": "sha512-uG511ggnnsw6gcn/X+YKkWPo5ep9il9wYi3QJxHsYe7yTZ4+cOd1wuodOUmOpFuXL+/RE3R04LczdNCDygTDgQ==", - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" - } - }, - "apps/laboratory/node_modules/tailwind-merge": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.2.2.tgz", - "integrity": "sha512-tWANXsnmJzgw6mQ07nE3aCDkCK4QdT3ThPMCzawoYA2Pws7vSTCvz3Vrjg61jVUGfFZPJzxEP+NimbcW+EdaDw==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.24.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/dcastil" - } - }, - "apps/laboratory/node_modules/tailwindcss": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.3.tgz", - "integrity": "sha512-U7sxQk/n397Bmx4JHbJx/iSOOv5G+II3f1kpLpY2QeUv5DcPdcTsYLlusZfq1NthHS1c1cZoyFmmkex1rzke0A==", - "dev": true, - "dependencies": { - "@alloc/quick-lru": "^5.2.0", - "arg": "^5.0.2", - "chokidar": "^3.5.3", - "didyoumean": "^1.2.2", - "dlv": "^1.1.3", - "fast-glob": "^3.3.0", - "glob-parent": "^6.0.2", - "is-glob": "^4.0.3", - "jiti": "^1.21.0", - "lilconfig": "^2.1.0", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "object-hash": "^3.0.0", - "picocolors": "^1.0.0", - "postcss": "^8.4.23", - "postcss-import": "^15.1.0", - "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.1", - "postcss-nested": "^6.0.1", - "postcss-selector-parser": "^6.0.11", - "resolve": "^1.22.2", - "sucrase": "^3.32.0" - }, - "bin": { - "tailwind": "lib/cli.js", - "tailwindcss": "lib/cli.js" - }, - "engines": { - "node": ">=14.0.0" - } - }, "apps/laboratory/node_modules/viem": { "version": "2.9.3", "resolved": "https://registry.npmjs.org/viem/-/viem-2.9.3.tgz", @@ -1011,6 +864,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, "engines": { "node": ">=10" }, @@ -6982,6 +6836,7 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.0.tgz", "integrity": "sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==", + "dev": true, "dependencies": { "@floating-ui/utils": "^0.2.1" } @@ -6990,6 +6845,7 @@ "version": "1.6.3", "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.3.tgz", "integrity": "sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw==", + "dev": true, "dependencies": { "@floating-ui/core": "^1.0.0", "@floating-ui/utils": "^0.2.0" @@ -6999,6 +6855,7 @@ "version": "2.0.8", "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.8.tgz", "integrity": "sha512-HOdqOt3R3OGeTKidaLvJKcgg75S6tibQ3Tif4eyd91QnIJWr0NLvoXFpJA/j8HqkFSL68GDca9AuyWEHlhyClw==", + "dev": true, "dependencies": { "@floating-ui/dom": "^1.6.1" }, @@ -7010,7 +6867,8 @@ "node_modules/@floating-ui/utils": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz", - "integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==" + "integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==", + "dev": true }, "node_modules/@gitbeaker/core": { "version": "35.8.1", @@ -8720,6 +8578,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.0.1.tgz", "integrity": "sha512-T5gIdVO2mmPW3NNhjNgEP3cqMXjXL9UbO0BzWcXfvdBs+BohbQxvd/K5hSVKmn9/lbTdsQVKbUcP5WLCwvUbBg==", + "dev": true, "dependencies": { "@babel/runtime": "^7.13.10" } @@ -8736,6 +8595,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.0.3.tgz", "integrity": "sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==", + "dev": true, "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-primitive": "1.0.3" @@ -8759,6 +8619,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.3.tgz", "integrity": "sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==", + "dev": true, "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-compose-refs": "1.0.1", @@ -8954,6 +8815,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.0.1.tgz", "integrity": "sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==", + "dev": true, "dependencies": { "@babel/runtime": "^7.13.10" }, @@ -8995,35 +8857,6 @@ } } }, - "node_modules/@radix-ui/react-dropdown-menu": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.0.6.tgz", - "integrity": "sha512-i6TuFOoWmLWq+M/eCLGd/bQ2HfAX1RJgvrBQ6AQLmzfvsLdefxbWu8G9zczcPFfcSPehz9GcpF6K9QYreFV8hA==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-id": "1.0.1", - "@radix-ui/react-menu": "2.0.6", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-controllable-state": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-focus-guards": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.1.tgz", @@ -9067,14 +8900,6 @@ } } }, - "node_modules/@radix-ui/react-icons": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-icons/-/react-icons-1.3.0.tgz", - "integrity": "sha512-jQxj/0LKgp+j9BiTXz3O3sgs26RNet2iLWmsPyRz2SIcR4q/4SbazXfnYwbAr+vLYKSfc7qxzyGQA1HLlYiuNw==", - "peerDependencies": { - "react": "^16.x || ^17.x || ^18.x" - } - }, "node_modules/@radix-ui/react-id": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.1.tgz", @@ -9093,177 +8918,6 @@ } } }, - "node_modules/@radix-ui/react-menu": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.0.6.tgz", - "integrity": "sha512-BVkFLS+bUC8HcImkRKPSiVumA1VPOOEC5WBMiT+QAVsPzW1FJzI9KnqgGxVDPBcql5xXrHkD3JOVoXWEXD8SYA==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-collection": "1.0.3", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-direction": "1.0.1", - "@radix-ui/react-dismissable-layer": "1.0.5", - "@radix-ui/react-focus-guards": "1.0.1", - "@radix-ui/react-focus-scope": "1.0.4", - "@radix-ui/react-id": "1.0.1", - "@radix-ui/react-popper": "1.1.3", - "@radix-ui/react-portal": "1.0.4", - "@radix-ui/react-presence": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-roving-focus": "1.0.4", - "@radix-ui/react-slot": "1.0.2", - "@radix-ui/react-use-callback-ref": "1.0.1", - "aria-hidden": "^1.1.1", - "react-remove-scroll": "2.5.5" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.5.tgz", - "integrity": "sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-callback-ref": "1.0.1", - "@radix-ui/react-use-escape-keydown": "1.0.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-focus-scope": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.4.tgz", - "integrity": "sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-callback-ref": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-popper": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.3.tgz", - "integrity": "sha512-cKpopj/5RHZWjrbF2846jBNacjQVwkP068DfmgrNJXpvVWrOvlAmE9xSiy5OqeE+Gi8D9fP+oDhUnPqNMY8/5w==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@floating-ui/react-dom": "^2.0.0", - "@radix-ui/react-arrow": "1.0.3", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-callback-ref": "1.0.1", - "@radix-ui/react-use-layout-effect": "1.0.1", - "@radix-ui/react-use-rect": "1.0.1", - "@radix-ui/react-use-size": "1.0.1", - "@radix-ui/rect": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-portal": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.4.tgz", - "integrity": "sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-primitive": "1.0.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-menu/node_modules/react-remove-scroll": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz", - "integrity": "sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==", - "dependencies": { - "react-remove-scroll-bar": "^2.3.3", - "react-style-singleton": "^2.2.1", - "tslib": "^2.1.0", - "use-callback-ref": "^1.3.0", - "use-sidecar": "^1.1.2" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-popper": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.2.tgz", @@ -9372,6 +9026,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.0.4.tgz", "integrity": "sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==", + "dev": true, "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/primitive": "1.0.1", @@ -9492,39 +9147,6 @@ } } }, - "node_modules/@radix-ui/react-slider": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slider/-/react-slider-1.1.2.tgz", - "integrity": "sha512-NKs15MJylfzVsCagVSWKhGGLNR1W9qWs+HtgbmjjVUB3B9+lb3PYoXxVju3kOrpf0VKyVCtZp+iTwVoqpa1Chw==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/number": "1.0.1", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-collection": "1.0.3", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-direction": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-controllable-state": "1.0.1", - "@radix-ui/react-use-layout-effect": "1.0.1", - "@radix-ui/react-use-previous": "1.0.1", - "@radix-ui/react-use-size": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-slot": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz", @@ -9749,6 +9371,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.0.1.tgz", "integrity": "sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==", + "dev": true, "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/rect": "1.0.1" @@ -9809,6 +9432,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.0.1.tgz", "integrity": "sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==", + "dev": true, "dependencies": { "@babel/runtime": "^7.13.10" } @@ -15772,7 +15396,8 @@ "node_modules/any-promise": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==" + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true }, "node_modules/anymatch": { "version": "3.1.3", @@ -15805,7 +15430,8 @@ "node_modules/arg": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true }, "node_modules/argparse": { "version": "2.0.1", @@ -16848,6 +16474,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, "engines": { "node": ">= 6" } @@ -17056,25 +16683,6 @@ "consola": "^3.2.3" } }, - "node_modules/class-variance-authority": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.0.tgz", - "integrity": "sha512-jFI8IQw4hczaL4ALINxqLEXQbWcNjoSkloa4IaufXCJr6QawJyw7tuRysRsrE8w2p/4gGaxKIt/hX3qz/IbD1A==", - "dependencies": { - "clsx": "2.0.0" - }, - "funding": { - "url": "https://joebell.co.uk" - } - }, - "node_modules/class-variance-authority/node_modules/clsx": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz", - "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==", - "engines": { - "node": ">=6" - } - }, "node_modules/clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -17735,6 +17343,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, "bin": { "cssesc": "bin/cssesc" }, @@ -18358,7 +17967,8 @@ "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true }, "node_modules/diff-sequences": { "version": "29.6.3", @@ -18388,7 +17998,8 @@ "node_modules/dlv": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true }, "node_modules/doctrine": { "version": "3.0.0", @@ -20794,6 +20405,7 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, "dependencies": { "is-glob": "^4.0.3" }, @@ -23050,6 +22662,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "dev": true, "engines": { "node": ">=10" } @@ -24415,6 +24028,7 @@ "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", @@ -24537,15 +24151,6 @@ "node": ">= 0.6" } }, - "node_modules/next-themes": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.3.0.tgz", - "integrity": "sha512-/QHIrsYpd6Kfk7xakK4svpDI5mmXP0gfvCoJdGpZQ2TOrQZmsW0QxjaiLn8wbIKjtm4BTSqLoix4lxYYOnLJ/w==", - "peerDependencies": { - "react": "^16.8 || ^17 || ^18", - "react-dom": "^16.8 || ^17 || ^18" - } - }, "node_modules/next/node_modules/@next/env": { "version": "14.1.1", "resolved": "https://registry.npmjs.org/@next/env/-/env-14.1.1.tgz", @@ -25848,6 +25453,7 @@ "version": "8.4.35", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", + "devOptional": true, "funding": [ { "type": "opencollective", @@ -25875,6 +25481,7 @@ "version": "15.1.0", "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", @@ -25891,6 +25498,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "dev": true, "dependencies": { "camelcase-css": "^2.0.1" }, @@ -25909,6 +25517,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "dev": true, "funding": [ { "type": "opencollective", @@ -25943,6 +25552,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz", "integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==", + "dev": true, "engines": { "node": ">=14" }, @@ -25954,6 +25564,7 @@ "version": "2.4.1", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.1.tgz", "integrity": "sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==", + "dev": true, "bin": { "yaml": "bin.mjs" }, @@ -25965,6 +25576,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", + "dev": true, "dependencies": { "postcss-selector-parser": "^6.0.11" }, @@ -25983,6 +25595,7 @@ "version": "6.0.16", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz", "integrity": "sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==", + "dev": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -25994,7 +25607,8 @@ "node_modules/postcss-value-parser": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true }, "node_modules/preact": { "version": "10.20.1", @@ -26927,6 +26541,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, "dependencies": { "pify": "^2.3.0" } @@ -26935,6 +26550,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -28585,6 +28201,7 @@ "version": "3.35.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "dev": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", @@ -28606,6 +28223,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, "engines": { "node": ">= 6" } @@ -28734,6 +28352,7 @@ "version": "3.4.1", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.1.tgz", "integrity": "sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==", + "dev": true, "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", @@ -28766,18 +28385,11 @@ "node": ">=14.0.0" } }, - "node_modules/tailwindcss-animate": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz", - "integrity": "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==", - "peerDependencies": { - "tailwindcss": ">=3.0.0 || insiders" - } - }, "node_modules/tailwindcss/node_modules/object-hash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, "engines": { "node": ">= 6" } @@ -29040,6 +28652,7 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, "dependencies": { "any-promise": "^1.0.0" } @@ -29048,6 +28661,7 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, "dependencies": { "thenify": ">= 3.1.0 < 4" }, @@ -29204,7 +28818,8 @@ "node_modules/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==" + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true }, "node_modules/ts-toolbelt": { "version": "9.6.0", diff --git a/packages/ui/src/utils/ThemeUtil.ts b/packages/ui/src/utils/ThemeUtil.ts index 37fb0518a6..25611a4d6e 100644 --- a/packages/ui/src/utils/ThemeUtil.ts +++ b/packages/ui/src/utils/ThemeUtil.ts @@ -456,16 +456,16 @@ function createRootStyles(themeVariables?: ThemeVariables) { light: css` :root { --w3m-color-mix: ${unsafeCSS(themeVariables?.['--w3m-color-mix'] || '#fff')}; - --w3m-accent: ${unsafeCSS(themeVariables?.['--w3m-accent'] || 'hsla(230, 100%, 67%, 1)')}; + --w3m-accent: ${unsafeCSS(themeVariables?.['--w3m-accent'] || '#47a1ff')}; --w3m-default: #fff; - --wui-color-modal-bg-base: #121313; + --wui-color-modal-bg-base: #191a1a; --wui-color-blue-base-100: #47a1ff; --wui-color-accent-base-100: var(--w3m-accent); - --wui-color-accent-base-090: hsla(231, 76%, 61%, 1); - --wui-color-accent-base-080: hsla(230, 59%, 55%, 1); + --wui-color-accent-base-090: #59aaff; + --wui-color-accent-base-080: #6cb4ff; --wui-accent-glass-base-090: rgba(71, 161, 255, 0.9); --wui-accent-glass-base-080: rgba(71, 161, 255, 0.8); @@ -547,7 +547,7 @@ function createRootStyles(themeVariables?: ThemeVariables) { dark: css` :root { --w3m-color-mix: ${unsafeCSS(themeVariables?.['--w3m-color-mix'] || '#000')}; - --w3m-accent: ${unsafeCSS(themeVariables?.['--w3m-accent'] || 'hsla(231, 100%, 70%, 1)')}; + --w3m-accent: ${unsafeCSS(themeVariables?.['--w3m-accent'] || '#3396ff')}; --w3m-default: #000; --wui-color-modal-bg-base: #fff; @@ -555,8 +555,8 @@ function createRootStyles(themeVariables?: ThemeVariables) { --wui-color-blue-base-100: #3396ff; --wui-color-accent-base-100: var(--w3m-accent); - --wui-color-accent-base-090: hsla(231, 97%, 72%, 1); - --wui-color-accent-base-080: hsla(231, 92%, 74%, 1); + --wui-color-accent-base-090: #2d7dd2; + --wui-color-accent-base-080: #2978cc; --wui-accent-glass-base-090: rgba(51, 150, 255, 0.9); --wui-accent-glass-base-080: rgba(51, 150, 255, 0.8); From 4bb1008af8533f8b177fb0bd09db1d8b9e6ab2d6 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Wed, 3 Apr 2024 14:03:26 +0300 Subject: [PATCH 63/96] chore: remove ts ignores, add mock functions for solana client --- packages/solana/src/client.ts | 15 +++++++++++++-- .../src/composites/wui-token-list-item/styles.ts | 2 +- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/packages/solana/src/client.ts b/packages/solana/src/client.ts index 5e5c3b9dc3..1bf9527bcf 100644 --- a/packages/solana/src/client.ts +++ b/packages/solana/src/client.ts @@ -90,7 +90,6 @@ export class Web3Modal extends Web3ModalScaffold { }) } - // @ts-ignore const connectionControllerClient: ConnectionControllerClient = { connectWalletConnect: async onUri => { const WalletConnectProvider = await this.WalletConnectConnector.getProvider() @@ -141,7 +140,19 @@ export class Web3Modal extends Web3ModalScaffold { }) return signature as string - } + }, + + sendTransaction: async () => { + return '0x' + }, + + getEstimatedGas: async () => { + return BigInt(0) + }, + + parseUnits: () => BigInt(0), + + formatUnits: () => '' } super({ diff --git a/packages/ui/src/composites/wui-token-list-item/styles.ts b/packages/ui/src/composites/wui-token-list-item/styles.ts index 8e5f1897a3..6b5aa1f3a2 100644 --- a/packages/ui/src/composites/wui-token-list-item/styles.ts +++ b/packages/ui/src/composites/wui-token-list-item/styles.ts @@ -3,7 +3,7 @@ import { css } from 'lit' export default css` :host > wui-flex { cursor: pointer; - display: block; + display: flex; column-gap: var(--wui-spacing-s); padding: var(--wui-spacing-xs); padding-right: var(--wui-spacing-l); From a07300c4f2654343e111333c4948fed1ff1b86c1 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Wed, 3 Apr 2024 14:10:19 +0300 Subject: [PATCH 64/96] chore: export missing comps --- packages/ui/src/utils/JSXTypeUtil.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/ui/src/utils/JSXTypeUtil.ts b/packages/ui/src/utils/JSXTypeUtil.ts index effabe86b5..c00c371c23 100644 --- a/packages/ui/src/utils/JSXTypeUtil.ts +++ b/packages/ui/src/utils/JSXTypeUtil.ts @@ -42,6 +42,8 @@ import type { WuiSearchBar } from '../composites/wui-search-bar/index.js' import type { WuiSnackbar } from '../composites/wui-snackbar/index.js' import type { WuiTabs } from '../composites/wui-tabs/index.js' import type { WuiTag } from '../composites/wui-tag/index.js' +import type { WuiTokenButton } from '../composites/wui-token-button/index.js' +import type { WuiTokenListItem } from '../composites/wui-token-list-item/index.js' import type { WuiTooltip } from '../composites/wui-tooltip/index.js' import type { WuiTransactionVisual } from '../composites/wui-transaction-visual/index.js' import type { WuiVisualThumbnail } from '../composites/wui-visual-thumbnail/index.js' @@ -116,6 +118,8 @@ declare global { 'wui-snackbar': CustomElement 'wui-tabs': CustomElement 'wui-tag': CustomElement + 'wui-token-button': CustomElement + 'wui-token-list-item': CustomElement 'wui-tooltip': CustomElement 'wui-transaction-visual': CustomElement 'wui-visual-thumbnail': CustomElement From 726e30e69eaf0e9b0a8221adc20a26666dfec049 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Wed, 3 Apr 2024 14:21:45 +0300 Subject: [PATCH 65/96] chore: convert controller tests --- packages/core/src/controllers/ConvertController.ts | 3 +-- packages/core/src/utils/ConstantsUtil.ts | 4 +++- packages/core/src/utils/ConvertApiUtil.ts | 5 ++--- .../core/tests/controllers/ConvertController.test.ts | 12 ++++++++++++ 4 files changed, 18 insertions(+), 6 deletions(-) create mode 100644 packages/core/tests/controllers/ConvertController.test.ts diff --git a/packages/core/src/controllers/ConvertController.ts b/packages/core/src/controllers/ConvertController.ts index 5624cece8c..b4ddb12ce0 100644 --- a/packages/core/src/controllers/ConvertController.ts +++ b/packages/core/src/controllers/ConvertController.ts @@ -8,7 +8,6 @@ import { SnackController } from './SnackController.js' import { RouterController } from './RouterController.js' import { NumberUtil } from '@web3modal/common' -export const DEFAULT_SLIPPAGE_TOLERANCE = '0.5' const INITIAL_GAS_LIMIT = 150000 // -- Types --------------------------------------------- // @@ -120,7 +119,7 @@ const state = proxy({ inputError: undefined, // Request values - slippage: DEFAULT_SLIPPAGE_TOLERANCE, + slippage: ConstantsUtil.CONVERT_SLIPPAGE_TOLERANCE, // Tokens tokens: undefined, diff --git a/packages/core/src/utils/ConstantsUtil.ts b/packages/core/src/utils/ConstantsUtil.ts index 6033300656..8aa5412370 100644 --- a/packages/core/src/utils/ConstantsUtil.ts +++ b/packages/core/src/utils/ConstantsUtil.ts @@ -153,7 +153,9 @@ export const ConstantsUtil = { 'WNT' ], - NATIVE_TOKEN_ADDRESS: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee' + NATIVE_TOKEN_ADDRESS: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee', + + CONVERT_SLIPPAGE_TOLERANCE: '0.5' } export type CoinbasePaySDKChainNameValues = diff --git a/packages/core/src/utils/ConvertApiUtil.ts b/packages/core/src/utils/ConvertApiUtil.ts index 0338fee964..66a9efe6a7 100644 --- a/packages/core/src/utils/ConvertApiUtil.ts +++ b/packages/core/src/utils/ConvertApiUtil.ts @@ -6,7 +6,6 @@ import { ConnectionController } from '../controllers/ConnectionController.js' import { ConstantsUtil } from '../utils/ConstantsUtil.js' const ONEINCH_API_BASE_URL = 'https://1inch-swap-proxy.walletconnect-v1-bridge.workers.dev' -export const DEFAULT_SLIPPAGE_TOLERANCE = '0.5' function get1InchEndpoints(chainId: number, address: string | undefined) { return { @@ -290,7 +289,7 @@ export const ConvertApiUtil = { params: { src: sourceTokenAddress, dst: toTokenAddress, - slippage: DEFAULT_SLIPPAGE_TOLERANCE, + slippage: ConstantsUtil.CONVERT_SLIPPAGE_TOLERANCE, from: fromAddress, amount: ConnectionController.parseUnits(sourceTokenAmount, decimals).toString() } @@ -322,7 +321,7 @@ export const ConvertApiUtil = { params: { src: sourceTokenAddress, dst: toTokenAddress, - slippage: DEFAULT_SLIPPAGE_TOLERANCE, + slippage: ConstantsUtil.CONVERT_SLIPPAGE_TOLERANCE, from: fromAddress, amount: ConnectionController.parseUnits(sourceTokenAmount, decimals).toString() } diff --git a/packages/core/tests/controllers/ConvertController.test.ts b/packages/core/tests/controllers/ConvertController.test.ts new file mode 100644 index 0000000000..52c6841da5 --- /dev/null +++ b/packages/core/tests/controllers/ConvertController.test.ts @@ -0,0 +1,12 @@ +import { describe, expect, it } from 'vitest' +import { ConvertController } from '../../index.js' + +// -- Tests -------------------------------------------------------------------- +describe('ConvertController', () => { + it('should have default state as expected', () => { + expect(ConvertController.state.initialized).toEqual(false) + expect(ConvertController.state.tokens).toEqual([]) + expect(ConvertController.state.sourceToken).toEqual(undefined) + expect(ConvertController.state.toToken).toEqual(undefined) + }) +}) From 47b1ca873051135855f28a6758b6afa91bf6094b Mon Sep 17 00:00:00 2001 From: enesozturk Date: Wed, 3 Apr 2024 14:26:55 +0300 Subject: [PATCH 66/96] chore: linter issues --- packages/core/src/controllers/ConvertController.ts | 2 -- packages/solana/src/client.ts | 4 ++-- packages/ui/src/composites/wui-details-group-item/index.ts | 2 +- packages/wagmi/src/client.ts | 2 ++ 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/core/src/controllers/ConvertController.ts b/packages/core/src/controllers/ConvertController.ts index b4ddb12ce0..05d432b8dd 100644 --- a/packages/core/src/controllers/ConvertController.ts +++ b/packages/core/src/controllers/ConvertController.ts @@ -480,7 +480,6 @@ export const ConvertController = { .dividedBy(state.toTokenPriceInUSD) : NumberUtil.bigNumber(0) - // return toTokenConvertedAmount with 18 decimals return toTokenConvertedAmount.multipliedBy(multiplyer).toString() }, @@ -618,7 +617,6 @@ export const ConvertController = { return transactionHash } catch (err) { const error = err as TransactionError - console.log('>>> sendTransactionForConvert - error', error) state.transactionError = error?.shortMessage state.transactionLoading = false SnackController.showError(error?.shortMessage || 'Transaction error') diff --git a/packages/solana/src/client.ts b/packages/solana/src/client.ts index 1bf9527bcf..35dbfe94b4 100644 --- a/packages/solana/src/client.ts +++ b/packages/solana/src/client.ts @@ -143,11 +143,11 @@ export class Web3Modal extends Web3ModalScaffold { }, sendTransaction: async () => { - return '0x' + return await Promise.resolve('0x') }, getEstimatedGas: async () => { - return BigInt(0) + return await Promise.resolve(BigInt(0)) }, parseUnits: () => BigInt(0), diff --git a/packages/ui/src/composites/wui-details-group-item/index.ts b/packages/ui/src/composites/wui-details-group-item/index.ts index 12e95c9227..059c037590 100644 --- a/packages/ui/src/composites/wui-details-group-item/index.ts +++ b/packages/ui/src/composites/wui-details-group-item/index.ts @@ -10,7 +10,7 @@ export class WuiDetailsGroupItem extends LitElement { public static override styles = [resetStyles, elementStyles, styles] // -- State & Properties -------------------------------- // - @property() public name: string = '' + @property() public name = '' // -- Render -------------------------------------------- // public override render() { diff --git a/packages/wagmi/src/client.ts b/packages/wagmi/src/client.ts index cb46d58602..8874e31926 100644 --- a/packages/wagmi/src/client.ts +++ b/packages/wagmi/src/client.ts @@ -493,8 +493,10 @@ export class Web3Modal extends Web3ModalScaffold { }) provider.onRpcResponse(receive => { + // eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error, @typescript-eslint/ban-ts-comment // @ts-ignore const payload = receive?.payload + // eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error, @typescript-eslint/ban-ts-comment // @ts-ignore const isError = receive?.type === '@w3m-frame/RPC_REQUEST_ERROR' From 74486a15df263cad71e7ace17d558e6d846d7507 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Wed, 3 Apr 2024 21:18:47 +0300 Subject: [PATCH 67/96] refactor: update transaction and router logics --- .../core/src/controllers/ConvertController.ts | 33 +++++++++---------- .../core/src/controllers/RouterController.ts | 20 +++++++---- packages/scaffold/src/client.ts | 22 +++++++++++-- .../w3m-approve-transaction-view/index.ts | 2 +- packages/wagmi/src/client.ts | 23 ++++++++----- 5 files changed, 65 insertions(+), 35 deletions(-) diff --git a/packages/core/src/controllers/ConvertController.ts b/packages/core/src/controllers/ConvertController.ts index 05d432b8dd..0741317eb3 100644 --- a/packages/core/src/controllers/ConvertController.ts +++ b/packages/core/src/controllers/ConvertController.ts @@ -384,20 +384,18 @@ export const ConvertController = { return gasCostInUSD.toNumber() }, - calculatePriceImpact(_toTokenAmount: string, _gasPriceInUSD = 0) { - const toTokenAmount = NumberUtil.bigNumber(_toTokenAmount) - - const totalSourceCostUSD = NumberUtil.bigNumber(state.sourceTokenAmount).multipliedBy( - state.sourceTokenPriceInUSD - ) - const adjustedTotalSourceCostUSD = totalSourceCostUSD.plus(_gasPriceInUSD) - const effectivePricePerTargetToken = adjustedTotalSourceCostUSD.dividedBy( - toTokenAmount.toFixed(4) - ) - - const priceImpact = effectivePricePerTargetToken - .minus(state.toTokenPriceInUSD) - .dividedBy(state.toTokenPriceInUSD) + calculatePriceImpact(toTokenAmount: string, gasPriceInUSD: number) { + const sourceTokenAmount = state.sourceTokenAmount + const sourceTokenPrice = state.sourceTokenPriceInUSD + const toTokenPrice = state.toTokenPriceInUSD + + const totalCostInUSD = NumberUtil.bigNumber(sourceTokenAmount) + .multipliedBy(sourceTokenPrice) + .plus(gasPriceInUSD) + const effectivePricePerToToken = totalCostInUSD.dividedBy(toTokenAmount) + const priceImpact = effectivePricePerToToken + .minus(toTokenPrice) + .dividedBy(toTokenPrice) .multipliedBy(100) return priceImpact.toNumber() @@ -515,7 +513,7 @@ export const ConvertController = { const { fromAddress } = this.getParams() state.transactionLoading = true - RouterController.pushTransactionSuccessView({ + RouterController.pushTransactionStack({ view: null, goBack: true }) @@ -590,7 +588,8 @@ export const ConvertController = { const { fromAddress } = this.getParams() state.transactionLoading = true - RouterController.pushTransactionSuccessView({ + + RouterController.pushTransactionStack({ view: 'Account', goBack: false, callback: () => { @@ -651,7 +650,7 @@ export const ConvertController = { state.inputError = insufficientBalance ? 'Insufficient balance' : undefined } - state.priceImpact = this.calculatePriceImpact(transaction.toAmount, state.gasPriceInUSD) + state.priceImpact = this.calculatePriceImpact(state.toTokenAmount, state.gasPriceInUSD) state.maxSlippage = this.calculateMaxSlippage() } } diff --git a/packages/core/src/controllers/RouterController.ts b/packages/core/src/controllers/RouterController.ts index 4547431ca8..2ab918717e 100644 --- a/packages/core/src/controllers/RouterController.ts +++ b/packages/core/src/controllers/RouterController.ts @@ -3,9 +3,10 @@ import { proxy } from 'valtio/vanilla' import type { CaipNetwork, Connector, WcWallet } from '../utils/TypeUtil.js' // -- Types --------------------------------------------- // -type TransactionSuccessAction = { +type TransactionAction = { goBack: boolean view: RouterControllerState['view'] | null + close?: boolean callback?: () => void } export interface RouterControllerState { @@ -56,14 +57,14 @@ export interface RouterControllerState { newEmail?: string target?: 'sourceToken' | 'toToken' } - transactionSuccessStack: TransactionSuccessAction[] + transactionStack: TransactionAction[] } // -- State --------------------------------------------- // const state = proxy({ view: 'Connect', history: ['Connect'], - transactionSuccessStack: [] + transactionStack: [] }) type StateKey = keyof RouterControllerState @@ -76,17 +77,22 @@ export const RouterController = { return subKey(state, key, callback) }, - pushTransactionSuccessView(action: TransactionSuccessAction) { - state.transactionSuccessStack.push(action) + pushTransactionStack(action: TransactionAction) { + state.transactionStack.push(action) }, - popTransactionSuccessAction() { - const action = state.transactionSuccessStack.pop() + popTransactionStack(cancel?: boolean) { + const action = state.transactionStack.pop() if (!action) { return } + if (cancel) { + this.goBack() + return + } + if (action.goBack) { this.goBack() } else if (action.view) { diff --git a/packages/scaffold/src/client.ts b/packages/scaffold/src/client.ts index 3dd28bdf2f..48f024f153 100644 --- a/packages/scaffold/src/client.ts +++ b/packages/scaffold/src/client.ts @@ -9,7 +9,8 @@ import type { ThemeMode, ThemeVariables, ModalControllerState, - ConnectedWalletInfo + ConnectedWalletInfo, + RouterControllerState } from '@web3modal/core' import { AccountController, @@ -23,7 +24,8 @@ import { OptionsController, PublicStateController, ThemeController, - SnackController + SnackController, + RouterController } from '@web3modal/core' import { setColorTheme, setThemeVariables } from '@web3modal/ui' import type { SIWEControllerClient } from '@web3modal/siwe' @@ -83,6 +85,22 @@ export class Web3ModalScaffold { ModalController.close() } + public redirect(route: RouterControllerState['view']) { + RouterController.push(route) + } + + public popTransactionStack(cancel?: boolean) { + RouterController.popTransactionStack(cancel) + } + + public isOpen() { + return ModalController.state.open + } + + public isTransactionStackEmpty() { + return RouterController.state.transactionStack.length === 0 + } + public setLoading(loading: ModalControllerState['loading']) { ModalController.setLoading(loading) } diff --git a/packages/scaffold/src/views/w3m-approve-transaction-view/index.ts b/packages/scaffold/src/views/w3m-approve-transaction-view/index.ts index c43fa219dd..5d96399bc2 100644 --- a/packages/scaffold/src/views/w3m-approve-transaction-view/index.ts +++ b/packages/scaffold/src/views/w3m-approve-transaction-view/index.ts @@ -36,7 +36,7 @@ export class W3mApproveTransactionView extends LitElement { ModalController.subscribeKey('open', isOpen => { if (!isOpen) { this.onHideIframe() - RouterController.popTransactionSuccessAction() + RouterController.popTransactionStack() } }) ] diff --git a/packages/wagmi/src/client.ts b/packages/wagmi/src/client.ts index 8766b1fe27..5a6b3a4450 100644 --- a/packages/wagmi/src/client.ts +++ b/packages/wagmi/src/client.ts @@ -41,7 +41,6 @@ import { } from './utils/helpers.js' import { W3mFrameHelpers, W3mFrameRpcConstants } from '@web3modal/wallet' import type { W3mFrameProvider } from '@web3modal/wallet' -import { ModalController, RouterController } from '@web3modal/core' import { NetworkUtil } from '@web3modal/common' import type { defaultWagmiConfig as coreConfig } from './utils/defaultWagmiCoreConfig.js' import type { defaultWagmiConfig as reactConfig } from './utils/defaultWagmiReactConfig.js' @@ -481,9 +480,9 @@ export class Web3Modal extends Web3ModalScaffold { provider.onRpcRequest(request => { if (W3mFrameHelpers.checkIfRequestExists(request)) { if (!W3mFrameHelpers.checkIfRequestIsAllowed(request)) { - if (ModalController.state.open) { - if (RouterController.state.transactionSuccessStack?.length > 0) { - RouterController.push('ApproveTransaction') + if (super.isOpen()) { + if (!super.isTransactionStackEmpty()) { + super.redirect('ApproveTransaction') } } else { super.open({ view: 'ApproveTransaction' }) @@ -509,16 +508,24 @@ export class Web3Modal extends Web3ModalScaffold { // @ts-ignore const isError = receive?.type === '@w3m-frame/RPC_REQUEST_ERROR' - if (isError) { - RouterController.popTransactionSuccessAction() + if (isError && super.isOpen()) { + if (super.isTransactionStackEmpty()) { + super.close() + } else { + super.popTransactionStack(true) + } } const isPayloadString = typeof payload === 'string' const isAddress = isPayloadString ? payload?.startsWith('0x') : false const isCompleted = isAddress && payload?.length > 10 - if (isCompleted && RouterController.state.transactionSuccessStack?.length > 0) { - RouterController.popTransactionSuccessAction() + if (isCompleted) { + if (super.isTransactionStackEmpty()) { + super.close() + } else { + super.popTransactionStack() + } } }) From b738d98954aa3591baaf4767cb5b4fcc971e9a7e Mon Sep 17 00:00:00 2001 From: enesozturk Date: Wed, 3 Apr 2024 21:27:09 +0300 Subject: [PATCH 68/96] chore: make sure about component exports --- packages/ui/src/utils/JSXTypeUtil.ts | 48 +++++++++++++++------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/packages/ui/src/utils/JSXTypeUtil.ts b/packages/ui/src/utils/JSXTypeUtil.ts index c00c371c23..6c8aebc29b 100644 --- a/packages/ui/src/utils/JSXTypeUtil.ts +++ b/packages/ui/src/utils/JSXTypeUtil.ts @@ -11,32 +11,47 @@ import type { WuiVisual } from '../components/wui-visual/index.js' import type { WuiAccountButton } from '../composites/wui-account-button/index.js' import type { WuiAllWalletsImage } from '../composites/wui-all-wallets-image/index.js' import type { WuiAvatar } from '../composites/wui-avatar/index.js' +import type { WuiBalance } from '../composites/wui-balance/index.js' +import type { WuiBanner } from '../composites/wui-banner/index.js' import type { WuiButton } from '../composites/wui-button/index.js' -import type { WuiCardSelectLoader } from '../composites/wui-card-select-loader/index.js' import type { WuiCardSelect } from '../composites/wui-card-select/index.js' +import type { WuiCardSelectLoader } from '../composites/wui-card-select-loader/index.js' import type { WuiChip } from '../composites/wui-chip/index.js' -import type { WuiConvertInput } from '../composites/wui-convert-input/index.js' -import type { WuiConvertDetails } from '../composites/wui-convert-details/index.js' +import type { WuiCompatibleNetwork } from '../composites/wui-compatible-network/index.js' +import type { WuiChipButton } from '../composites/wui-chip-button/index.js' import type { WuiConnectButton } from '../composites/wui-connect-button/index.js' +import type { WuiConvertDetails } from '../composites/wui-convert-details/index.js' +import type { WuiConvertInput } from '../composites/wui-convert-input/index.js' import type { WuiCtaButton } from '../composites/wui-cta-button/index.js' import type { WuiDetailsGroup } from '../composites/wui-details-group/index.js' import type { WuiDetailsGroupItem } from '../composites/wui-details-group-item/index.js' import type { WuiEmailInput } from '../composites/wui-email-input/index.js' import type { WuiIconBox } from '../composites/wui-icon-box/index.js' import type { WuiIconLink } from '../composites/wui-icon-link/index.js' +import type { WuiInputAmount } from '../composites/wui-input-amount/index.js' import type { WuiInputElement } from '../composites/wui-input-element/index.js' import type { WuiInputNumeric } from '../composites/wui-input-numeric/index.js' import type { WuiInputText } from '../composites/wui-input-text/index.js' import type { WuiLink } from '../composites/wui-link/index.js' +import type { WuiListAccordion } from '../composites/wui-list-accordion/index.js' +import type { WuiListContent } from '../composites/wui-list-content/index.js' +import type { WuiListDescription } from '../composites/wui-list-description/index.js' import type { WuiListItem } from '../composites/wui-list-item/index.js' -import type { WuiTransactionListItem } from '../composites/wui-transaction-list-item/index.js' -import type { WuiTransactionListItemLoader } from '../composites/wui-transaction-list-item-loader/index.js' +import type { WuiListNetwork } from '../composites/wui-list-network/index.js' +import type { WuiListToken } from '../composites/wui-list-token/index.js' import type { WuiListWallet } from '../composites/wui-list-wallet/index.js' -import type { WuiLogoSelect } from '../composites/wui-logo-select/index.js' +import type { WuiListWalletTransaction } from '../composites/wui-list-wallet-transaction/index.js' import type { WuiLogo } from '../composites/wui-logo/index.js' +import type { WuiLogoSelect } from '../composites/wui-logo-select/index.js' import type { WuiNetworkButton } from '../composites/wui-network-button/index.js' import type { WuiNetworkImage } from '../composites/wui-network-image/index.js' +import type { WuiNoticeCard } from '../composites/wui-notice-card/index.js' +import type { WuiOnRampActivityItem } from '../composites/wui-onramp-activity-item/index.js' +import type { WuiOnRampProviderItem } from '../composites/wui-onramp-provider-item/index.js' import type { WuiOtp } from '../composites/wui-otp/index.js' +import type { WuiPreviewItem } from '../composites/wui-preview-item/index.js' +import type { WuiProfileButton } from '../composites/wui-profile-button/index.js' +import type { WuiPromo } from '../composites/wui-promo/index.js' import type { WuiQrCode } from '../composites/wui-qr-code/index.js' import type { WuiSearchBar } from '../composites/wui-search-bar/index.js' import type { WuiSnackbar } from '../composites/wui-snackbar/index.js' @@ -45,25 +60,12 @@ import type { WuiTag } from '../composites/wui-tag/index.js' import type { WuiTokenButton } from '../composites/wui-token-button/index.js' import type { WuiTokenListItem } from '../composites/wui-token-list-item/index.js' import type { WuiTooltip } from '../composites/wui-tooltip/index.js' +import type { WuiTooltipSelect } from '../composites/wui-tooltip-select/index.js' +import type { WuiTransactionListItem } from '../composites/wui-transaction-list-item/index.js' +import type { WuiTransactionListItemLoader } from '../composites/wui-transaction-list-item-loader/index.js' import type { WuiTransactionVisual } from '../composites/wui-transaction-visual/index.js' import type { WuiVisualThumbnail } from '../composites/wui-visual-thumbnail/index.js' import type { WuiWalletImage } from '../composites/wui-wallet-image/index.js' -import type { WuiNoticeCard } from '../composites/wui-notice-card/index.js' -import type { WuiListAccordion } from '../composites/wui-list-accordion/index.js' -import type { WuiListContent } from '../composites/wui-list-content/index.js' -import type { WuiListNetwork } from '../composites/wui-list-network/index.js' -import type { WuiListWalletTransaction } from '../composites/wui-list-wallet-transaction/index.js' -import type { WuiOnRampActivityItem } from '../composites/wui-onramp-activity-item/index.js' -import type { WuiOnRampProviderItem } from '../composites/wui-onramp-provider-item/index.js' -import type { WuiPromo } from '../composites/wui-promo/index.js' -import type { WuiBalance } from '../composites/wui-balance/index.js' -import type { WuiTooltipSelect } from '../composites/wui-tooltip-select/index.js' -import type { WuiProfileButton } from '../composites/wui-profile-button/index.js' -import type { WuiBanner } from '../composites/wui-banner/index.js' -import type { WuiCompatibleNetwork } from '../composites/wui-compatible-network/index.js' -import type { WuiListToken } from '../composites/wui-list-token/index.js' -import type { WuiListDescription } from '../composites/wui-list-description/index.js' -import type { WuiPreviewItem } from '../composites/wui-preview-item/index.js' import type { WuiFlex } from '../layout/wui-flex/index.js' import type { WuiGrid } from '../layout/wui-grid/index.js' @@ -91,6 +93,7 @@ declare global { 'wui-card-select-loader': CustomElement 'wui-card-select': CustomElement 'wui-chip': CustomElement + 'wui-chip-button': CustomElement 'wui-convert-input': CustomElement 'wui-convert-details': CustomElement 'wui-connect-button': CustomElement @@ -100,6 +103,7 @@ declare global { 'wui-email-input': CustomElement 'wui-icon-box': CustomElement 'wui-icon-link': CustomElement + 'wui-icon-amount': CustomElement 'wui-input-element': CustomElement 'wui-input-numeric': CustomElement 'wui-input-text': CustomElement From 25b1a7d3207cf9568a7935302f78074dc98d163b Mon Sep 17 00:00:00 2001 From: enesozturk Date: Wed, 3 Apr 2024 21:40:06 +0300 Subject: [PATCH 69/96] chore: update transaction stack callbacks --- .../core/src/controllers/ConvertController.ts | 2 +- .../core/src/controllers/RouterController.ts | 19 ++++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/packages/core/src/controllers/ConvertController.ts b/packages/core/src/controllers/ConvertController.ts index 0741317eb3..b3a8f36292 100644 --- a/packages/core/src/controllers/ConvertController.ts +++ b/packages/core/src/controllers/ConvertController.ts @@ -592,7 +592,7 @@ export const ConvertController = { RouterController.pushTransactionStack({ view: 'Account', goBack: false, - callback: () => { + onSuccess() { ConvertController.resetValues() } }) diff --git a/packages/core/src/controllers/RouterController.ts b/packages/core/src/controllers/RouterController.ts index 2ab918717e..168025a416 100644 --- a/packages/core/src/controllers/RouterController.ts +++ b/packages/core/src/controllers/RouterController.ts @@ -7,7 +7,8 @@ type TransactionAction = { goBack: boolean view: RouterControllerState['view'] | null close?: boolean - callback?: () => void + onSuccess?: () => void + onCancel?: () => void } export interface RouterControllerState { view: @@ -90,15 +91,15 @@ export const RouterController = { if (cancel) { this.goBack() - return - } - - if (action.goBack) { - this.goBack() - } else if (action.view) { - this.reset(action.view) + action?.onCancel?.() + } else { + if (action.goBack) { + this.goBack() + } else if (action.view) { + this.reset(action.view) + } + action?.onSuccess?.() } - action.callback?.() }, push(view: RouterControllerState['view'], data?: RouterControllerState['data']) { From 42584182650b7092aaf3f979ffa4c4b6c861dd66 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Wed, 3 Apr 2024 21:44:09 +0300 Subject: [PATCH 70/96] chore: add missing exported composites --- packages/ui/src/utils/JSXTypeUtil.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui/src/utils/JSXTypeUtil.ts b/packages/ui/src/utils/JSXTypeUtil.ts index 6c8aebc29b..c23b320b1f 100644 --- a/packages/ui/src/utils/JSXTypeUtil.ts +++ b/packages/ui/src/utils/JSXTypeUtil.ts @@ -103,7 +103,7 @@ declare global { 'wui-email-input': CustomElement 'wui-icon-box': CustomElement 'wui-icon-link': CustomElement - 'wui-icon-amount': CustomElement + 'wui-input-amount': CustomElement 'wui-input-element': CustomElement 'wui-input-numeric': CustomElement 'wui-input-text': CustomElement From d054a5c361bfdf043a6ee26a845aed15642e89e8 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Wed, 3 Apr 2024 23:37:57 +0300 Subject: [PATCH 71/96] chore: update lock file --- package-lock.json | 661 +++++++++++++++++++---------------- packages/wagmi/src/client.ts | 2 + 2 files changed, 354 insertions(+), 309 deletions(-) diff --git a/package-lock.json b/package-lock.json index dc75a3e376..3794c5403d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -907,6 +907,23 @@ "x-default-browser": "bin/x-default-browser.js" } }, + "node_modules/@aws-crypto/crc32": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-3.0.0.tgz", + "integrity": "sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==", + "dev": true, + "dependencies": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/crc32/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, "node_modules/@aws-crypto/ie11-detection": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz", @@ -1556,25 +1573,25 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.1.tgz", - "integrity": "sha512-Pc65opHDliVpRHuKfzI+gSA4zcgr65O4cl64fFJIWEEh8JoHIHh0Oez1Eo8Arz8zq/JhgKodQaxEwUPRtZylVA==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.4.tgz", + "integrity": "sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.24.3", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.3.tgz", - "integrity": "sha512-5FcvN1JHw2sHJChotgx8Ek0lyuh4kCKelgMTTqhYJJtloNvUfpAFMeNQUtdlIaktwrSV9LtCdqwk48wL2wBacQ==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.4.tgz", + "integrity": "sha512-MBVlMXP+kkl5394RBLSxxk/iLTeVGuXTV3cIDXavPpMMqnSnt6apKgan/U8O3USWZCWZT/TbgfEpKa4uMgN4Dg==", "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.24.2", - "@babel/generator": "^7.24.1", + "@babel/generator": "^7.24.4", "@babel/helper-compilation-targets": "^7.23.6", "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.24.1", - "@babel/parser": "^7.24.1", + "@babel/helpers": "^7.24.4", + "@babel/parser": "^7.24.4", "@babel/template": "^7.24.0", "@babel/traverse": "^7.24.1", "@babel/types": "^7.24.0", @@ -1606,9 +1623,9 @@ } }, "node_modules/@babel/generator": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.1.tgz", - "integrity": "sha512-DfCRfZsBcrPEHUfuBMgbJ1Ut01Y/itOs+hY2nFLgqsqXd52/iSiVq5TITtUasIUgm+IIKdY2/1I7auiQOEeC9A==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.4.tgz", + "integrity": "sha512-Xd6+v6SnjWVx/nus+y0l1sxMOTOMBkyL4+BIdbALyatQnAe/SRVjANeDPSCYaX+i1iJmuGSKf3Z+E+V/va1Hvw==", "dependencies": { "@babel/types": "^7.24.0", "@jridgewell/gen-mapping": "^0.3.5", @@ -1665,9 +1682,9 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.1.tgz", - "integrity": "sha512-1yJa9dX9g//V6fDebXoEfEsxkZHk3Hcbm+zLhyu6qVgYFLvmTALTeV+jNU9e5RnYtioBrGEOdoI2joMSNQ/+aA==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.4.tgz", + "integrity": "sha512-lG75yeuUSVu0pIcbhiYMXBXANHrpUPaOfu7ryAzskCgKUHuAxRQI5ssrtmF0X9UXldPlvT0XM/A4F44OXRt6iQ==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-environment-visitor": "^7.22.20", @@ -1926,9 +1943,9 @@ } }, "node_modules/@babel/helpers": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.1.tgz", - "integrity": "sha512-BpU09QqEe6ZCHuIHFphEFgvNSrubve1FtyMton26ekZ85gRGi6LrTF7zArARp2YvyFxloeiRmtSCq5sjh1WqIg==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.4.tgz", + "integrity": "sha512-FewdlZbSiwaVGlgT1DPANDuCHaDMiOo+D/IDYRFYjHOuv66xMSJ7fQwwODwRNAPkADIO/z1EoF/l2BCWlWABDw==", "dependencies": { "@babel/template": "^7.24.0", "@babel/traverse": "^7.24.1", @@ -2017,9 +2034,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.1.tgz", - "integrity": "sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.4.tgz", + "integrity": "sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==", "bin": { "parser": "bin/babel-parser.js" }, @@ -2027,6 +2044,21 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.24.4.tgz", + "integrity": "sha512-qpl6vOOEEzTLLcsuqYYo8yDtrTocmu2xkGvgNebvPjT9DTtfFYGmgDqY+rBYXNlqL4s9qLDn6xkrJv4RxAPiTA==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { "version": "7.24.1", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.24.1.tgz", @@ -2560,9 +2592,9 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.1.tgz", - "integrity": "sha512-h71T2QQvDgM2SmT29UYU6ozjMlAt7s7CSs5Hvy8f8cf/GM/Z4a2zMfN+fjVGaieeCrXR3EdQl6C4gQG+OgmbKw==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.4.tgz", + "integrity": "sha512-nIFUZIpGKDf9O9ttyRXpHFpKC+X3Y5mtshZONuEUYBomAKoM4y029Jr+uB1bHGPhNmK8YXHevDtKDOLmtRrp6g==", "dependencies": { "@babel/helper-plugin-utils": "^7.24.0" }, @@ -2589,11 +2621,11 @@ } }, "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.1.tgz", - "integrity": "sha512-FUHlKCn6J3ERiu8Dv+4eoz7w8+kFLSyeVG4vDAikwADGjUCoHw/JHokyGtr8OR4UjpwPVivyF+h8Q5iv/JmrtA==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.4.tgz", + "integrity": "sha512-B8q7Pz870Hz/q9UgP8InNpY01CSLDSCyqX7zcRuv3FcPl87A2G17lASroHWaCtbdIcbYzOZ7kWmXFKbijMSmFg==", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.1", + "@babel/helper-create-class-features-plugin": "^7.24.4", "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-class-static-block": "^7.14.5" }, @@ -3277,12 +3309,12 @@ } }, "node_modules/@babel/plugin-transform-typescript": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.24.1.tgz", - "integrity": "sha512-liYSESjX2fZ7JyBFkYG78nfvHlMKE6IpNdTVnxmlYUR+j5ZLsitFbaAE+eJSK2zPPkNWNw4mXL51rQ8WrvdK0w==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.24.4.tgz", + "integrity": "sha512-79t3CQ8+oBGk/80SQ8MN3Bs3obf83zJ0YZjDmDaEZN8MqhMI760apl5z6a20kFeMXBwJX99VpKT8CKxEBp5H1g==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.24.1", + "@babel/helper-create-class-features-plugin": "^7.24.4", "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-typescript": "^7.24.1" }, @@ -3353,14 +3385,15 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.24.3", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.3.tgz", - "integrity": "sha512-fSk430k5c2ff8536JcPvPWK4tZDwehWLGlBp0wrsBUjZVdeQV6lePbwKWZaZfK2vnh/1kQX1PzAJWsnBmVgGJA==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.4.tgz", + "integrity": "sha512-7Kl6cSmYkak0FK/FXjSEnLJ1N9T/WA2RkMhu17gZ/dsxKJUuTYNIylahPTzqpLyJN4WhDif8X0XK1R8Wsguo/A==", "dependencies": { - "@babel/compat-data": "^7.24.1", + "@babel/compat-data": "^7.24.4", "@babel/helper-compilation-targets": "^7.23.6", "@babel/helper-plugin-utils": "^7.24.0", "@babel/helper-validator-option": "^7.23.5", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.24.4", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.24.1", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.1", "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.24.1", @@ -3387,9 +3420,9 @@ "@babel/plugin-transform-async-generator-functions": "^7.24.3", "@babel/plugin-transform-async-to-generator": "^7.24.1", "@babel/plugin-transform-block-scoped-functions": "^7.24.1", - "@babel/plugin-transform-block-scoping": "^7.24.1", + "@babel/plugin-transform-block-scoping": "^7.24.4", "@babel/plugin-transform-class-properties": "^7.24.1", - "@babel/plugin-transform-class-static-block": "^7.24.1", + "@babel/plugin-transform-class-static-block": "^7.24.4", "@babel/plugin-transform-classes": "^7.24.1", "@babel/plugin-transform-computed-properties": "^7.24.1", "@babel/plugin-transform-destructuring": "^7.24.1", @@ -3618,9 +3651,9 @@ "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" }, "node_modules/@babel/runtime": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.1.tgz", - "integrity": "sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.4.tgz", + "integrity": "sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -3670,9 +3703,9 @@ } }, "node_modules/@babel/types": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.4.tgz", - "integrity": "sha512-7uIFwVYpoplT5jp/kVv6EF93VaJ8H+Yn5IczYiaAi98ajzjfoZfslet/e0sLh+wVBjb2qqIut1b0S26VSafsSQ==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", + "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", "dependencies": { "@babel/helper-string-parser": "^7.23.4", "@babel/helper-validator-identifier": "^7.22.20", @@ -5566,9 +5599,9 @@ } }, "node_modules/@emotion/serialize": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.3.tgz", - "integrity": "sha512-iD4D6QVZFDhcbH0RAG1uVu1CwVLMWUkCvAqqlewO/rxf8+87yIBAlt4+AxMiiKPLs5hFc0owNk/sLLAOROw3cA==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.4.tgz", + "integrity": "sha512-RIN04MBT8g+FnDwgvIUi8czvr1LU1alUMI05LekWB5DGyTm8cCBMCRpq3GqaiyEDRptEXOyXnvZ58GZYu4kBxQ==", "dependencies": { "@emotion/hash": "^0.9.1", "@emotion/memoize": "^0.8.1", @@ -6961,9 +6994,9 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", - "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "dev": true }, "node_modules/@isaacs/cliui": { @@ -7298,9 +7331,9 @@ "integrity": "sha512-yWJKmpGE6lUURKAaIltoPIE/wrbY3TEkqQt+X0m+7fQNnAv0keydnYvbiJFP1PnMhizmIWRWOG5KLhYyc/xl+g==" }, "node_modules/@lit/reactive-element": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.0.2.tgz", - "integrity": "sha512-SVOwLAWUQg3Ji1egtOt1UiFe4zdDpnWHyc5qctSceJ5XIu0Uc76YmGpIjZgx9YJ0XtdW0Jm507sDvjOu+HnB8w==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.0.4.tgz", + "integrity": "sha512-GFn91inaUa2oHLak8awSIigYz0cU0Payr1rcFsrkf5OJ5eSPxElyZfKh0f2p9FsTiZWXQdWGJeXZICEfXXYSXQ==", "dependencies": { "@lit-labs/ssr-dom-shim": "^1.2.0" } @@ -7503,18 +7536,6 @@ "node": ">=14.0.0" } }, - "node_modules/@metamask/json-rpc-engine/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/@metamask/object-multiplex": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@metamask/object-multiplex/-/object-multiplex-1.3.0.tgz", @@ -7641,18 +7662,6 @@ "node": ">=14.0.0" } }, - "node_modules/@metamask/rpc-errors/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/@metamask/safe-event-emitter": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@metamask/safe-event-emitter/-/safe-event-emitter-2.0.0.tgz", @@ -7725,6 +7734,14 @@ "node-fetch": "^2.6.12" } }, + "node_modules/@metamask/sdk-communication-layer/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@metamask/sdk-install-modal-web": { "version": "0.14.1", "resolved": "https://registry.npmjs.org/@metamask/sdk-install-modal-web/-/sdk-install-modal-web-0.14.1.tgz", @@ -7783,6 +7800,14 @@ "@babel/runtime": "^7.20.6" } }, + "node_modules/@metamask/sdk/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@metamask/utils": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/@metamask/utils/-/utils-5.0.2.tgz", @@ -10237,9 +10262,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.2.tgz", - "integrity": "sha512-3XFIDKWMFZrMnao1mJhnOT1h2g0169Os848NhhmGweEcfJ4rCi+3yMCOLG4zA61rbJdkcrM/DjVZm9Hg5p5w7g==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.0.tgz", + "integrity": "sha512-jwXtxYbRt1V+CdQSy6Z+uZti7JF5irRKF8hlKfEnF/xJpcNGuuiZMBvuoYM+x9sr9iWGnzrlM0+9hvQ1kgkf1w==", "cpu": [ "arm" ], @@ -10250,9 +10275,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.13.2.tgz", - "integrity": "sha512-GdxxXbAuM7Y/YQM9/TwwP+L0omeE/lJAR1J+olu36c3LqqZEBdsIWeQ91KBe6nxwOnb06Xh7JS2U5ooWU5/LgQ==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.0.tgz", + "integrity": "sha512-fI9nduZhCccjzlsA/OuAwtFGWocxA4gqXGTLvOyiF8d+8o0fZUeSztixkYjcGq1fGZY3Tkq4yRvHPFxU+jdZ9Q==", "cpu": [ "arm64" ], @@ -10263,9 +10288,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.13.2.tgz", - "integrity": "sha512-mCMlpzlBgOTdaFs83I4XRr8wNPveJiJX1RLfv4hggyIVhfB5mJfN4P8Z6yKh+oE4Luz+qq1P3kVdWrCKcMYrrA==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.0.tgz", + "integrity": "sha512-BcnSPRM76/cD2gQC+rQNGBN6GStBs2pl/FpweW8JYuz5J/IEa0Fr4AtrPv766DB/6b2MZ/AfSIOSGw3nEIP8SA==", "cpu": [ "arm64" ], @@ -10276,9 +10301,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.13.2.tgz", - "integrity": "sha512-yUoEvnH0FBef/NbB1u6d3HNGyruAKnN74LrPAfDQL3O32e3k3OSfLrPgSJmgb3PJrBZWfPyt6m4ZhAFa2nZp2A==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.0.tgz", + "integrity": "sha512-LDyFB9GRolGN7XI6955aFeI3wCdCUszFWumWU0deHA8VpR3nWRrjG6GtGjBrQxQKFevnUTHKCfPR4IvrW3kCgQ==", "cpu": [ "x64" ], @@ -10289,9 +10314,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.13.2.tgz", - "integrity": "sha512-GYbLs5ErswU/Xs7aGXqzc3RrdEjKdmoCrgzhJWyFL0r5fL3qd1NPcDKDowDnmcoSiGJeU68/Vy+OMUluRxPiLQ==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.0.tgz", + "integrity": "sha512-ygrGVhQP47mRh0AAD0zl6QqCbNsf0eTo+vgwkY6LunBcg0f2Jv365GXlDUECIyoXp1kKwL5WW6rsO429DBY/bA==", "cpu": [ "arm" ], @@ -10302,9 +10327,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.13.2.tgz", - "integrity": "sha512-L1+D8/wqGnKQIlh4Zre9i4R4b4noxzH5DDciyahX4oOz62CphY7WDWqJoQ66zNR4oScLNOqQJfNSIAe/6TPUmQ==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.0.tgz", + "integrity": "sha512-x+uJ6MAYRlHGe9wi4HQjxpaKHPM3d3JjqqCkeC5gpnnI6OWovLdXTpfa8trjxPLnWKyBsSi5kne+146GAxFt4A==", "cpu": [ "arm64" ], @@ -10315,9 +10340,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.13.2.tgz", - "integrity": "sha512-tK5eoKFkXdz6vjfkSTCupUzCo40xueTOiOO6PeEIadlNBkadH1wNOH8ILCPIl8by/Gmb5AGAeQOFeLev7iZDOA==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.0.tgz", + "integrity": "sha512-nrRw8ZTQKg6+Lttwqo6a2VxR9tOroa2m91XbdQ2sUUzHoedXlsyvY1fN4xWdqz8PKmf4orDwejxXHjh7YBGUCA==", "cpu": [ "arm64" ], @@ -10328,9 +10353,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.13.2.tgz", - "integrity": "sha512-zvXvAUGGEYi6tYhcDmb9wlOckVbuD+7z3mzInCSTACJ4DQrdSLPNUeDIcAQW39M3q6PDquqLWu7pnO39uSMRzQ==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.0.tgz", + "integrity": "sha512-xV0d5jDb4aFu84XKr+lcUJ9y3qpIWhttO3Qev97z8DKLXR62LC3cXT/bMZXrjLF9X+P5oSmJTzAhqwUbY96PnA==", "cpu": [ "ppc64le" ], @@ -10341,9 +10366,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.13.2.tgz", - "integrity": "sha512-C3GSKvMtdudHCN5HdmAMSRYR2kkhgdOfye4w0xzyii7lebVr4riCgmM6lRiSCnJn2w1Xz7ZZzHKuLrjx5620kw==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.0.tgz", + "integrity": "sha512-SDDhBQwZX6LPRoPYjAZWyL27LbcBo7WdBFWJi5PI9RPCzU8ijzkQn7tt8NXiXRiFMJCVpkuMkBf4OxSxVMizAw==", "cpu": [ "riscv64" ], @@ -10354,9 +10379,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.13.2.tgz", - "integrity": "sha512-l4U0KDFwzD36j7HdfJ5/TveEQ1fUTjFFQP5qIt9gBqBgu1G8/kCaq5Ok05kd5TG9F8Lltf3MoYsUMw3rNlJ0Yg==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.0.tgz", + "integrity": "sha512-RxB/qez8zIDshNJDufYlTT0ZTVut5eCpAZ3bdXDU9yTxBzui3KhbGjROK2OYTTor7alM7XBhssgoO3CZ0XD3qA==", "cpu": [ "s390x" ], @@ -10367,9 +10392,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.13.2.tgz", - "integrity": "sha512-xXMLUAMzrtsvh3cZ448vbXqlUa7ZL8z0MwHp63K2IIID2+DeP5iWIT6g1SN7hg1VxPzqx0xZdiDM9l4n9LRU1A==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.0.tgz", + "integrity": "sha512-C6y6z2eCNCfhZxT9u+jAM2Fup89ZjiG5pIzZIDycs1IwESviLxwkQcFRGLjnDrP+PT+v5i4YFvlcfAs+LnreXg==", "cpu": [ "x64" ], @@ -10380,9 +10405,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.13.2.tgz", - "integrity": "sha512-M/JYAWickafUijWPai4ehrjzVPKRCyDb1SLuO+ZyPfoXgeCEAlgPkNXewFZx0zcnoIe3ay4UjXIMdXQXOZXWqA==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.0.tgz", + "integrity": "sha512-i0QwbHYfnOMYsBEyjxcwGu5SMIi9sImDVjDg087hpzXqhBSosxkE7gyIYFHgfFl4mr7RrXksIBZ4DoLoP4FhJg==", "cpu": [ "x64" ], @@ -10393,9 +10418,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.13.2.tgz", - "integrity": "sha512-2YWwoVg9KRkIKaXSh0mz3NmfurpmYoBBTAXA9qt7VXk0Xy12PoOP40EFuau+ajgALbbhi4uTj3tSG3tVseCjuA==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.0.tgz", + "integrity": "sha512-Fq52EYb0riNHLBTAcL0cun+rRwyZ10S9vKzhGKKgeD+XbwunszSY0rVMco5KbOsTlwovP2rTOkiII/fQ4ih/zQ==", "cpu": [ "arm64" ], @@ -10406,9 +10431,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.13.2.tgz", - "integrity": "sha512-2FSsE9aQ6OWD20E498NYKEQLneShWes0NGMPQwxWOdws35qQXH+FplabOSP5zEe1pVjurSDOGEVCE2agFwSEsw==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.0.tgz", + "integrity": "sha512-e/PBHxPdJ00O9p5Ui43+vixSgVf4NlLsmV6QneGERJ3lnjIua/kim6PRFe3iDueT1rQcgSkYP8ZBBXa/h4iPvw==", "cpu": [ "ia32" ], @@ -10419,9 +10444,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.13.2.tgz", - "integrity": "sha512-7h7J2nokcdPePdKykd8wtc8QqqkqxIrUz7MHj6aNr8waBRU//NLDVnNjQnqQO6fqtjrtCdftpbTuOKAyrAQETQ==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.0.tgz", + "integrity": "sha512-aGg7iToJjdklmxlUlJh/PaPNa4PmqHfyRMLunbL3eaMO0gp656+q1zOKkpJ/CVe9CryJv6tAN1HDoR8cNGzkag==", "cpu": [ "x64" ], @@ -10738,13 +10763,13 @@ } }, "node_modules/@smithy/core": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-1.4.0.tgz", - "integrity": "sha512-uu9ZDI95Uij4qk+L6kyFjdk11zqBkcJ3Lv0sc6jZrqHvLyr0+oeekD3CnqMafBn/5PRI6uv6ulW3kNLRBUHeVw==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-1.4.1.tgz", + "integrity": "sha512-jCnbEQHvTOUQXxXOS110FIMc83dCXUlrqiG/q0QzUSYhglDj9bJVPFjXmxc6qUfARe0mEb8h9LeVoh7FUYHuUg==", "dev": true, "dependencies": { "@smithy/middleware-endpoint": "^2.5.0", - "@smithy/middleware-retry": "^2.2.0", + "@smithy/middleware-retry": "^2.3.0", "@smithy/middleware-serde": "^2.3.0", "@smithy/protocol-http": "^3.3.0", "@smithy/smithy-client": "^2.5.0", @@ -10867,9 +10892,9 @@ } }, "node_modules/@smithy/middleware-retry": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-2.2.0.tgz", - "integrity": "sha512-PsjDOLpbevgn37yJbawmfVoanru40qVA8UEf2+YA1lvOefmhuhL6ZbKtGsLAWDRnE1OlAmedsbA/htH6iSZjNA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-2.3.0.tgz", + "integrity": "sha512-5H7kD0My2RkZryvYIWA4C9w6t/pdJfbgEdq+fcZhbnZsqHm/4vYFVjDsOzb5pC7PEpksuijoM9fGbM6eN4rLSg==", "dev": true, "dependencies": { "@smithy/node-config-provider": "^2.3.0", @@ -10880,7 +10905,7 @@ "@smithy/util-middleware": "^2.2.0", "@smithy/util-retry": "^2.2.0", "tslib": "^2.6.2", - "uuid": "^8.3.2" + "uuid": "^9.0.1" }, "engines": { "node": ">=14.0.0" @@ -11475,18 +11500,6 @@ "@solana/web3.js": "*" } }, - "node_modules/@solflare-wallet/metamask-sdk/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/@solflare-wallet/sdk": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/@solflare-wallet/sdk/-/sdk-1.4.2.tgz", @@ -11500,18 +11513,6 @@ "@solana/web3.js": "*" } }, - "node_modules/@solflare-wallet/sdk/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/@spruceid/siwe-parser": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@spruceid/siwe-parser/-/siwe-parser-2.0.2.tgz", @@ -11687,19 +11688,6 @@ "url": "https://opencollective.com/storybook" } }, - "node_modules/@storybook/addon-actions/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "dev": true, - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/@storybook/addon-backgrounds": { "version": "7.6.7", "resolved": "https://registry.npmjs.org/@storybook/addon-backgrounds/-/addon-backgrounds-7.6.7.tgz", @@ -12444,9 +12432,9 @@ } }, "node_modules/@storybook/core-common/node_modules/@types/node": { - "version": "18.19.26", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.26.tgz", - "integrity": "sha512-+wiMJsIwLOYCvUqSdKTrfkS8mpTp+MPINe6+Np4TAGFWWRWiBQ5kSq9nZGCSPkzx9mvT+uEukzpX4MOSCydcvw==", + "version": "18.19.29", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.29.tgz", + "integrity": "sha512-5pAX7ggTmWZdhUrhRWLPf+5oM7F80bcKVCBbr0zwEkTNzTJL2CWQjznpFgHYy6GrzkYi2Yjy7DHKoynFxqPV8g==", "dependencies": { "undici-types": "~5.26.4" } @@ -12570,9 +12558,9 @@ } }, "node_modules/@storybook/core-server/node_modules/@types/node": { - "version": "18.19.26", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.26.tgz", - "integrity": "sha512-+wiMJsIwLOYCvUqSdKTrfkS8mpTp+MPINe6+Np4TAGFWWRWiBQ5kSq9nZGCSPkzx9mvT+uEukzpX4MOSCydcvw==", + "version": "18.19.29", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.29.tgz", + "integrity": "sha512-5pAX7ggTmWZdhUrhRWLPf+5oM7F80bcKVCBbr0zwEkTNzTJL2CWQjznpFgHYy6GrzkYi2Yjy7DHKoynFxqPV8g==", "dependencies": { "undici-types": "~5.26.4" } @@ -13130,6 +13118,15 @@ "@types/responselike": "^1.0.0" } }, + "node_modules/@types/chrome": { + "version": "0.0.136", + "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.136.tgz", + "integrity": "sha512-XDEiRhLkMd+SB7Iw3ZUIj/fov3wLd4HyTdLltVszkgl1dBfc3Rb7oPMVZ2Mz2TLqnF7Ow+StbR8E7r9lqpb4DA==", + "dependencies": { + "@types/filesystem": "*", + "@types/har-format": "*" + } + }, "node_modules/@types/connect": { "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", @@ -13208,6 +13205,19 @@ "@types/send": "*" } }, + "node_modules/@types/filesystem": { + "version": "0.0.36", + "resolved": "https://registry.npmjs.org/@types/filesystem/-/filesystem-0.0.36.tgz", + "integrity": "sha512-vPDXOZuannb9FZdxgHnqSwAG/jvdGM8Wq+6N4D/d80z+D4HWH+bItqsZaVRQykAn6WEVeEkLm2oQigyHtgb0RA==", + "dependencies": { + "@types/filewriter": "*" + } + }, + "node_modules/@types/filewriter": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/@types/filewriter/-/filewriter-0.0.33.tgz", + "integrity": "sha512-xFU8ZXTw4gd358lb2jw25nxY9QAgqn2+bKKjKOYfNCzN4DKCFetK7sPtrlpg66Ywe3vWY9FNxprZawAh9wfJ3g==" + }, "node_modules/@types/find-cache-dir": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/@types/find-cache-dir/-/find-cache-dir-3.2.1.tgz", @@ -13232,6 +13242,11 @@ "@types/node": "*" } }, + "node_modules/@types/har-format": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@types/har-format/-/har-format-1.2.15.tgz", + "integrity": "sha512-RpQH4rXLuvTXKR0zqHq3go0RVXYv/YVqv4TnPH95VbwUxZdQlK1EtcMvQvMpDngHbt13Csh9Z4qT9AbkiQH5BA==" + }, "node_modules/@types/http-cache-semantics": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", @@ -13435,9 +13450,9 @@ } }, "node_modules/@types/semver": { - "version": "7.5.6", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", - "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==" + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==" }, "node_modules/@types/send": { "version": "0.17.4", @@ -13449,13 +13464,13 @@ } }, "node_modules/@types/serve-static": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz", - "integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==", + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", + "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", "dependencies": { "@types/http-errors": "*", - "@types/mime": "*", - "@types/node": "*" + "@types/node": "*", + "@types/send": "*" } }, "node_modules/@types/stack-utils": { @@ -14174,9 +14189,9 @@ "integrity": "sha512-tr7XntDAu50BVENgQfajMLzacmSe34D+qZc4zjnniz0ZVuw/TZcLcyxHQjYpJTM36sGEkZZlYLnIM1hH7alTMA==" }, "node_modules/@walletconnect/core": { - "version": "2.11.3", - "resolved": "https://registry.npmjs.org/@walletconnect/core/-/core-2.11.3.tgz", - "integrity": "sha512-/9m4EqiggFUwkQDv5PDWbcTI+yCVnBd/iYW5iIHEkivg2/mnBr2bQz2r/vtPjp19r/ZK62Dx0+UN3U+BWP8ulQ==", + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@walletconnect/core/-/core-2.12.0.tgz", + "integrity": "sha512-CORck4dRvCpIn6hl2ZtUnjrSJ0JHt9TRteGCViwPyXNSuvXz70RvaIkvPoybYZBGCRQR4WTJ4dMdqeQpuyrL/g==", "dependencies": { "@walletconnect/heartbeat": "1.2.1", "@walletconnect/jsonrpc-provider": "1.0.13", @@ -14184,13 +14199,13 @@ "@walletconnect/jsonrpc-utils": "1.0.8", "@walletconnect/jsonrpc-ws-connection": "1.0.14", "@walletconnect/keyvaluestorage": "^1.1.1", - "@walletconnect/logger": "^2.0.1", + "@walletconnect/logger": "^2.1.0", "@walletconnect/relay-api": "^1.0.9", "@walletconnect/relay-auth": "^1.0.4", "@walletconnect/safe-json": "^1.0.2", "@walletconnect/time": "^1.0.2", - "@walletconnect/types": "2.11.3", - "@walletconnect/utils": "2.11.3", + "@walletconnect/types": "2.12.0", + "@walletconnect/utils": "2.12.0", "events": "^3.3.0", "isomorphic-unfetch": "3.1.0", "lodash.isequal": "4.5.0", @@ -14198,9 +14213,9 @@ } }, "node_modules/@walletconnect/core/node_modules/@walletconnect/types": { - "version": "2.11.3", - "resolved": "https://registry.npmjs.org/@walletconnect/types/-/types-2.11.3.tgz", - "integrity": "sha512-JY4wA9MVosDW9dcJMTpnwliste0aJGJ1X6Q4ulLsQsgWRSEBRkLila0oUT01TDBW9Yq8uUp7uFOUTaKx6KWVAg==", + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@walletconnect/types/-/types-2.12.0.tgz", + "integrity": "sha512-uhB3waGmujQVJcPgJvGOpB8RalgYSBT+HpmVbfl4Qe0xJyqpRUo4bPjQa0UYkrHaW20xIw94OuP4+FMLYdeemg==", "dependencies": { "@walletconnect/events": "^1.0.1", "@walletconnect/heartbeat": "1.2.1", @@ -14500,19 +14515,14 @@ } }, "node_modules/@walletconnect/logger": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@walletconnect/logger/-/logger-2.0.1.tgz", - "integrity": "sha512-SsTKdsgWm+oDTBeNE/zHxxr5eJfZmE9/5yp/Ku+zJtcTAjELb3DXueWkDXmE9h8uHIbJzIb5wj5lPdzyrjT6hQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@walletconnect/logger/-/logger-2.1.0.tgz", + "integrity": "sha512-lyCRHlxlBHxvj1fJXa2YOW4whVNucPKF7Oc0D1UvYhfArpIIjlJJiTe5cLm8g4ZH4z5lKp14N/c9oRHlyv5v4A==", "dependencies": { - "pino": "7.11.0", - "tslib": "1.14.1" + "@walletconnect/safe-json": "^1.0.2", + "pino": "7.11.0" } }, - "node_modules/@walletconnect/logger/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, "node_modules/@walletconnect/mobile-registry": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@walletconnect/mobile-registry/-/mobile-registry-1.4.0.tgz", @@ -14830,25 +14840,25 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "node_modules/@walletconnect/sign-client": { - "version": "2.11.3", - "resolved": "https://registry.npmjs.org/@walletconnect/sign-client/-/sign-client-2.11.3.tgz", - "integrity": "sha512-JVjLTxN/3NjMXv5zalSGKuSYLRyU2yX6AWEdq17cInlrwODpbWZr6PS1uxMWdH4r90DXBLhdtwDbEq/pfd0BPg==", + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@walletconnect/sign-client/-/sign-client-2.12.0.tgz", + "integrity": "sha512-JUHJVZtW9iJmn3I2byLzhMRSFiQicTPU92PLuHIF2nG98CqsvlPn8Cu8Cx5CEPFrxPQWwLA+Dv/F+wuSgQiD/w==", "dependencies": { - "@walletconnect/core": "2.11.3", + "@walletconnect/core": "2.12.0", "@walletconnect/events": "^1.0.1", "@walletconnect/heartbeat": "1.2.1", "@walletconnect/jsonrpc-utils": "1.0.8", "@walletconnect/logger": "^2.0.1", "@walletconnect/time": "^1.0.2", - "@walletconnect/types": "2.11.3", - "@walletconnect/utils": "2.11.3", + "@walletconnect/types": "2.12.0", + "@walletconnect/utils": "2.12.0", "events": "^3.3.0" } }, "node_modules/@walletconnect/sign-client/node_modules/@walletconnect/types": { - "version": "2.11.3", - "resolved": "https://registry.npmjs.org/@walletconnect/types/-/types-2.11.3.tgz", - "integrity": "sha512-JY4wA9MVosDW9dcJMTpnwliste0aJGJ1X6Q4ulLsQsgWRSEBRkLila0oUT01TDBW9Yq8uUp7uFOUTaKx6KWVAg==", + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@walletconnect/types/-/types-2.12.0.tgz", + "integrity": "sha512-uhB3waGmujQVJcPgJvGOpB8RalgYSBT+HpmVbfl4Qe0xJyqpRUo4bPjQa0UYkrHaW20xIw94OuP4+FMLYdeemg==", "dependencies": { "@walletconnect/events": "^1.0.1", "@walletconnect/heartbeat": "1.2.1", @@ -14997,9 +15007,9 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "node_modules/@walletconnect/utils": { - "version": "2.11.3", - "resolved": "https://registry.npmjs.org/@walletconnect/utils/-/utils-2.11.3.tgz", - "integrity": "sha512-jsdNkrl/IcTkzWFn0S2d0urzBXg6RxVJtUYRsUx3qI3wzOGiABP9ui3yiZ3SgZOv9aRe62PaNp1qpbYZ+zPb8Q==", + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@walletconnect/utils/-/utils-2.12.0.tgz", + "integrity": "sha512-GIpfHUe1Bjp1Tjda0SkJEizKOT2biuv7VPFnKsOLT1T+8QxEP9NruC+K2UUEvijS1Qr/LKH9P5004RYNgrch+w==", "dependencies": { "@stablelib/chacha20poly1305": "1.0.1", "@stablelib/hkdf": "1.0.1", @@ -15009,7 +15019,7 @@ "@walletconnect/relay-api": "^1.0.9", "@walletconnect/safe-json": "^1.0.2", "@walletconnect/time": "^1.0.2", - "@walletconnect/types": "2.11.3", + "@walletconnect/types": "2.12.0", "@walletconnect/window-getters": "^1.0.1", "@walletconnect/window-metadata": "^1.0.1", "detect-browser": "5.3.0", @@ -15018,9 +15028,9 @@ } }, "node_modules/@walletconnect/utils/node_modules/@walletconnect/types": { - "version": "2.11.3", - "resolved": "https://registry.npmjs.org/@walletconnect/types/-/types-2.11.3.tgz", - "integrity": "sha512-JY4wA9MVosDW9dcJMTpnwliste0aJGJ1X6Q4ulLsQsgWRSEBRkLila0oUT01TDBW9Yq8uUp7uFOUTaKx6KWVAg==", + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@walletconnect/types/-/types-2.12.0.tgz", + "integrity": "sha512-uhB3waGmujQVJcPgJvGOpB8RalgYSBT+HpmVbfl4Qe0xJyqpRUo4bPjQa0UYkrHaW20xIw94OuP4+FMLYdeemg==", "dependencies": { "@walletconnect/events": "^1.0.1", "@walletconnect/heartbeat": "1.2.1", @@ -16506,9 +16516,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001600", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001600.tgz", - "integrity": "sha512-+2S9/2JFhYmYaDpZvo0lKkfvuKIglrx68MwOBqMGHhQsNkLjB5xtc/TGoEPs+MxjSyN/72qer2g97nzR641mOQ==", + "version": "1.0.30001605", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001605.tgz", + "integrity": "sha512-nXwGlFWo34uliI9z3n6Qc0wZaf7zaZWA1CPZ169La5mV3I/gem7bst0vr5XQH5TJXZIMfDeZyOrZnSlVzKxxHQ==", "funding": [ { "type": "opencollective", @@ -16676,9 +16686,9 @@ } }, "node_modules/citty": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.5.tgz", - "integrity": "sha512-AS7n5NSc0OQVMV9v6wt3ByujNIrne0/cTjiC2MYqhvao57VNfiuVksTSr2p17nVOhEr2KtqiAkGwHcgMC/qUuQ==", + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", + "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", "dependencies": { "consola": "^3.2.3" } @@ -17218,9 +17228,9 @@ } }, "node_modules/cookie-es": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.0.0.tgz", - "integrity": "sha512-mWYvfOLrfEc996hlKcdABeIiPHUPC6DM2QYZdGGOvhOTbA3tjm2eBwqlJpoFdjC89NI4Qt6h0Pu06Mp+1Pj5OQ==" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.1.0.tgz", + "integrity": "sha512-L2rLOcK0wzWSfSDA33YR+PUHDG10a8px7rUHKWbGLP4YfbsMed2KFUw5fczvDPbT98DDe3LEzviswl810apTEw==" }, "node_modules/cookie-signature": { "version": "1.0.6", @@ -18100,9 +18110,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.721", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.721.tgz", - "integrity": "sha512-k1x2r6foI8iJOp+1qTxbbrrWMsOiHkzGBYwYigaq+apO1FSqtn44KTo3Sy69qt7CRr7149zTcsDvH7MUKsOuIQ==" + "version": "1.4.724", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.724.tgz", + "integrity": "sha512-RTRvkmRkGhNBPPpdrgtDKvmOEYTrPlXDfc0J/Nfq5s29tEahAwhiX4mmhNzj6febWMleulxVYPh7QwCSL/EldA==" }, "node_modules/elliptic": { "version": "6.5.4", @@ -18283,9 +18293,9 @@ } }, "node_modules/es-abstract": { - "version": "1.23.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.2.tgz", - "integrity": "sha512-60s3Xv2T2p1ICykc7c+DNDPLDMm9t4QxCOUU0K9JxiLjM3C1zB9YVdN7tjxrFd4+AkZ8CdX1ovUga4P2+1e+/w==", + "version": "1.23.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", + "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", "dev": true, "dependencies": { "array-buffer-byte-length": "^1.0.1", @@ -18327,11 +18337,11 @@ "safe-regex-test": "^1.0.3", "string.prototype.trim": "^1.2.9", "string.prototype.trimend": "^1.0.8", - "string.prototype.trimstart": "^1.0.7", + "string.prototype.trimstart": "^1.0.8", "typed-array-buffer": "^1.0.2", "typed-array-byte-length": "^1.0.1", "typed-array-byte-offset": "^1.0.2", - "typed-array-length": "^1.0.5", + "typed-array-length": "^1.0.6", "unbox-primitive": "^1.0.2", "which-typed-array": "^1.1.15" }, @@ -19943,9 +19953,9 @@ "peer": true }, "node_modules/flow-parser": { - "version": "0.222.0", - "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.222.0.tgz", - "integrity": "sha512-Fq5OkFlFRSMV2EOZW+4qUYMTE0uj8pcLsYJMxXYriSBDpHAF7Ofx3PibCTy3cs5P6vbsry7eYj7Z7xFD49GIOQ==", + "version": "0.206.0", + "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.206.0.tgz", + "integrity": "sha512-HVzoK3r6Vsg+lKvlIZzaWNBVai+FXTX1wdYhz/wVlH13tb/gOdLXmlTqy6odmTBhT5UoWUbq0k8263Qhr9d88w==", "engines": { "node": ">=0.4.0" } @@ -20571,16 +20581,18 @@ } }, "node_modules/h3": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/h3/-/h3-1.9.0.tgz", - "integrity": "sha512-+F3ZqrNV/CFXXfZ2lXBINHi+rM4Xw3CDC5z2CDK3NMPocjonKipGLLDSkrqY9DOrioZNPTIdDMWfQKm//3X2DA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/h3/-/h3-1.11.1.tgz", + "integrity": "sha512-AbaH6IDnZN6nmbnJOH72y3c5Wwh9P97soSVdGSBbcDACRdkC0FEWf25pzx4f/NuOCK6quHmW18yF2Wx+G4Zi1A==", "dependencies": { "cookie-es": "^1.0.0", - "defu": "^6.1.3", - "destr": "^2.0.2", + "crossws": "^0.2.2", + "defu": "^6.1.4", + "destr": "^2.0.3", "iron-webcrypto": "^1.0.0", + "ohash": "^1.1.3", "radix3": "^1.1.0", - "ufo": "^1.3.2", + "ufo": "^1.4.0", "uncrypto": "^0.1.3", "unenv": "^1.9.0" } @@ -20921,9 +20933,9 @@ } }, "node_modules/i18next-browser-languagedetector": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-7.2.0.tgz", - "integrity": "sha512-U00DbDtFIYD3wkWsr2aVGfXGAj2TgnELzOX9qv8bT0aJtvPV9CRO77h+vgmHFBMe7LAxdwvT/7VkCWGya6L3tA==", + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-7.2.1.tgz", + "integrity": "sha512-h/pM34bcH6tbz8WgGXcmWauNpQupCGr25XPp9cZwZInR9XHSjIFDYp1SIok7zSPsTOMxdvuLyu86V+g2Kycnfw==", "dependencies": { "@babel/runtime": "^7.23.2" } @@ -20964,9 +20976,9 @@ ] }, "node_modules/ignore": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", - "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", "engines": { "node": ">= 4" } @@ -21921,6 +21933,14 @@ "node": ">=6.14.2" } }, + "node_modules/jayson/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/jayson/node_modules/ws": { "version": "7.5.9", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", @@ -22181,9 +22201,9 @@ } }, "node_modules/joi": { - "version": "17.12.2", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.12.2.tgz", - "integrity": "sha512-RonXAIzCiHLc8ss3Ibuz45u28GOsWE1UpfDXLbN/9NKbL4tCJf8TWYVKsoYuuh+sAUt7fsSNpA+r2+TBA6Wjmw==", + "version": "17.12.3", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.12.3.tgz", + "integrity": "sha512-2RRziagf555owrm9IRVtdKynOBeITiDpuZqIpgwqXShPncPKNiRQoiGsl/T8SQdq+8ugRzH2LqY67irr2y/d+g==", "peer": true, "dependencies": { "@hapi/hoek": "^9.3.0", @@ -22712,19 +22732,19 @@ } }, "node_modules/lit-element": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-4.0.2.tgz", - "integrity": "sha512-/W6WQZUa5VEXwC7H9tbtDMdSs9aWil3Ou8hU6z2cOKWbsm/tXPAcsoaHVEtrDo0zcOIE5GF6QgU55tlGL2Nihg==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-4.0.4.tgz", + "integrity": "sha512-98CvgulX6eCPs6TyAIQoJZBCQPo80rgXR+dVBs61cstJXqtI+USQZAbA4gFHh6L/mxBx9MrgPLHLsUgDUHAcCQ==", "dependencies": { - "@lit-labs/ssr-dom-shim": "^1.1.2", - "@lit/reactive-element": "^2.0.0", - "lit-html": "^3.1.0" + "@lit-labs/ssr-dom-shim": "^1.2.0", + "@lit/reactive-element": "^2.0.4", + "lit-html": "^3.1.2" } }, "node_modules/lit-html": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.1.0.tgz", - "integrity": "sha512-FwAjq3iNsaO6SOZXEIpeROlJLUlrbyMkn4iuv4f4u1H40Jw8wkeR/OUXZUHUoiYabGk8Y4Y0F/rgq+R4MrOLmA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.1.2.tgz", + "integrity": "sha512-3OBZSUrPnAHoKJ9AMjRL/m01YJxQMf+TMHanNtTHG68ubjnZxK0RFl102DPzsw4mWnHibfZIBJm3LWCZ/LmMvg==", "dependencies": { "@types/trusted-types": "^2.0.2" } @@ -24151,6 +24171,14 @@ "node": ">= 0.6" } }, + "node_modules/next-auth/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/next/node_modules/@next/env": { "version": "14.1.1", "resolved": "https://registry.npmjs.org/@next/env/-/env-14.1.1.tgz", @@ -24388,9 +24416,9 @@ } }, "node_modules/node-gyp-build": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.7.1.tgz", - "integrity": "sha512-wTSrZ+8lsRRa3I3H8Xr65dLWSgCvY2l4AOnaeKdPA9TB/WYMPaTcrzf3rXvFoVvjKNVnu0CcWSx54qq9GKRUYg==", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.0.tgz", + "integrity": "sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og==", "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", @@ -25215,9 +25243,9 @@ } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", - "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", "engines": { "node": "14 || >=16.14" } @@ -27193,6 +27221,14 @@ "node": ">=6.14.2" } }, + "node_modules/rpc-websockets/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -27898,9 +27934,9 @@ } }, "node_modules/std-env": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.5.0.tgz", - "integrity": "sha512-JGUEaALvL0Mf6JCfYnJOTcobY+Nc7sG/TemDRBqCA0wEr4DER7zDchaaixTlmOxAjG1uRJmX82EQcxwTQTkqVA==" + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz", + "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==" }, "node_modules/store2": { "version": "2.14.3", @@ -28586,9 +28622,9 @@ } }, "node_modules/terser": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.30.0.tgz", - "integrity": "sha512-Y/SblUl5kEyEFzhMAQdsxVHh+utAxd4IuRNJzKywY/4uzSogh3G219jqbDDxYu4MXO9CzY3tSEqmZvW6AoEDJw==", + "version": "5.30.3", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.30.3.tgz", + "integrity": "sha512-STdUgOUx8rLbMGO9IOwHLpCqolkDITFFQSMYYwKE1N2lY6MVSaeoi10z/EhWxRc6ybqoVmKSkhKYH/XUpl7vSA==", "peer": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", @@ -28763,9 +28799,9 @@ } }, "node_modules/tocbot": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/tocbot/-/tocbot-4.23.0.tgz", - "integrity": "sha512-5DWuSZXsqG894mkGb8ZsQt9myyQyVxE50AiGRZ0obV0BVUTVkaZmc9jbgpknaAAPUm4FIrzGkEseD6FuQJYJDQ==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/tocbot/-/tocbot-4.25.0.tgz", + "integrity": "sha512-kE5wyCQJ40hqUaRVkyQ4z5+4juzYsv/eK+aqD97N62YH0TxFhzJvo22RUQQZdO3YnXAk42ZOfOpjVdy+Z0YokA==", "dev": true }, "node_modules/toggle-selection": { @@ -29170,9 +29206,9 @@ "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" }, "node_modules/types-ramda": { - "version": "0.29.6", - "resolved": "https://registry.npmjs.org/types-ramda/-/types-ramda-0.29.6.tgz", - "integrity": "sha512-VJoOk1uYNh9ZguGd3eZvqkdhD4hTGtnjRBUx5Zc0U9ftmnCgiWcSj/lsahzKunbiwRje1MxxNkEy1UdcXRCpYw==", + "version": "0.29.10", + "resolved": "https://registry.npmjs.org/types-ramda/-/types-ramda-0.29.10.tgz", + "integrity": "sha512-5PJiW/eiTPyXXBYGZOYGezMl6qj7keBiZheRwfjJZY26QPHsNrjfJnz0mru6oeqqoTHOni893Jfd6zyUXfQRWg==", "dev": true, "dependencies": { "ts-toolbelt": "^9.6.0" @@ -29242,14 +29278,14 @@ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, "node_modules/unenv": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/unenv/-/unenv-1.8.0.tgz", - "integrity": "sha512-uIGbdCWZfhRRmyKj1UioCepQ0jpq638j/Cf0xFTn4zD1nGJ2lSdzYHLzfdXN791oo/0juUiSWW1fBklXMTsuqg==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/unenv/-/unenv-1.9.0.tgz", + "integrity": "sha512-QKnFNznRxmbOF1hDgzpqrlIf6NC5sbZ2OJ+5Wl3OX8uM+LUJXbj4TXvLJCtwbPTmbMHCLIz6JLKNinNsMShK9g==", "dependencies": { "consola": "^3.2.3", "defu": "^6.1.3", "mime": "^3.0.0", - "node-fetch-native": "^1.4.1", + "node-fetch-native": "^1.6.1", "pathe": "^1.1.1" } }, @@ -29372,10 +29408,13 @@ "integrity": "sha512-d6Mhq8RJeGA8UfKCu54Um4lFA0eSaRa3XxdAJg8tIdxbu1ubW0hBCZUL7yI2uGyYCRndvbK8FLHzqy2XKfeMsg==", "dev": true, "dependencies": { - "acorn": "^8.11.2", - "chokidar": "^3.5.3", + "acorn": "^8.11.3", + "chokidar": "^3.6.0", "webpack-sources": "^3.2.3", - "webpack-virtual-modules": "^0.6.0" + "webpack-virtual-modules": "^0.6.1" + }, + "engines": { + "node": ">=14.0.0" } }, "node_modules/unstorage": { @@ -29452,9 +29491,9 @@ } }, "node_modules/unstorage/node_modules/lru-cache": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", - "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", "engines": { "node": "14 || >=16.14" } @@ -29622,9 +29661,13 @@ } }, "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], "bin": { "uuid": "dist/bin/uuid" } @@ -30204,9 +30247,9 @@ } }, "node_modules/vite/node_modules/rollup": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.2.tgz", - "integrity": "sha512-MIlLgsdMprDBXC+4hsPgzWUasLO9CE4zOkj/u6j+Z6j5A4zRY+CtiXAdJyPtgCsc42g658Aeh1DlrdVEJhsL2g==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.14.0.tgz", + "integrity": "sha512-Qe7w62TyawbDzB4yt32R0+AbIo6m1/sqO7UPzFS8Z/ksL5mrfhA0v4CavfdmFav3D+ub4QeAgsGEe84DoWe/nQ==", "dev": true, "dependencies": { "@types/estree": "1.0.5" @@ -30219,21 +30262,21 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.13.2", - "@rollup/rollup-android-arm64": "4.13.2", - "@rollup/rollup-darwin-arm64": "4.13.2", - "@rollup/rollup-darwin-x64": "4.13.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.13.2", - "@rollup/rollup-linux-arm64-gnu": "4.13.2", - "@rollup/rollup-linux-arm64-musl": "4.13.2", - "@rollup/rollup-linux-powerpc64le-gnu": "4.13.2", - "@rollup/rollup-linux-riscv64-gnu": "4.13.2", - "@rollup/rollup-linux-s390x-gnu": "4.13.2", - "@rollup/rollup-linux-x64-gnu": "4.13.2", - "@rollup/rollup-linux-x64-musl": "4.13.2", - "@rollup/rollup-win32-arm64-msvc": "4.13.2", - "@rollup/rollup-win32-ia32-msvc": "4.13.2", - "@rollup/rollup-win32-x64-msvc": "4.13.2", + "@rollup/rollup-android-arm-eabi": "4.14.0", + "@rollup/rollup-android-arm64": "4.14.0", + "@rollup/rollup-darwin-arm64": "4.14.0", + "@rollup/rollup-darwin-x64": "4.14.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.14.0", + "@rollup/rollup-linux-arm64-gnu": "4.14.0", + "@rollup/rollup-linux-arm64-musl": "4.14.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.14.0", + "@rollup/rollup-linux-riscv64-gnu": "4.14.0", + "@rollup/rollup-linux-s390x-gnu": "4.14.0", + "@rollup/rollup-linux-x64-gnu": "4.14.0", + "@rollup/rollup-linux-x64-musl": "4.14.0", + "@rollup/rollup-win32-arm64-msvc": "4.14.0", + "@rollup/rollup-win32-ia32-msvc": "4.14.0", + "@rollup/rollup-win32-x64-msvc": "4.14.0", "fsevents": "~2.3.2" } }, diff --git a/packages/wagmi/src/client.ts b/packages/wagmi/src/client.ts index 5a6b3a4450..b6e32bd343 100644 --- a/packages/wagmi/src/client.ts +++ b/packages/wagmi/src/client.ts @@ -203,6 +203,8 @@ export class Web3Modal extends Web3ModalScaffold { type: 'legacy' as const } + // eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error, @typescript-eslint/ban-ts-comment + // @ts-ignore await prepareTransactionRequest(this.wagmiConfig, txParams) const tx = await wagmiSendTransaction(this.wagmiConfig, txParams) From 0f43f7b0f6e449fc2fb8e9b3cc41ad4b99462906 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Fri, 5 Apr 2024 15:45:24 +0300 Subject: [PATCH 72/96] chore: update token fetch listeners --- packages/core/src/controllers/ConvertController.ts | 10 +++++++--- packages/scaffold/src/views/w3m-convert-view/index.ts | 5 ++++- packages/wallet/src/W3mFrame.ts | 2 ++ 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/core/src/controllers/ConvertController.ts b/packages/core/src/controllers/ConvertController.ts index b3a8f36292..b56052fba3 100644 --- a/packages/core/src/controllers/ConvertController.ts +++ b/packages/core/src/controllers/ConvertController.ts @@ -259,13 +259,17 @@ export const ConvertController = { async initializeState() { if (!state.initialized) { - await this.getTokenList() - await this.getNetworkTokenPrice() - await this.getMyTokensWithBalance() + await this.fetchTokens() state.initialized = true } }, + async fetchTokens() { + await this.getTokenList() + await this.getNetworkTokenPrice() + await this.getMyTokensWithBalance() + }, + async getTokenList() { const res = await ConvertApiUtil.getTokenList() diff --git a/packages/scaffold/src/views/w3m-convert-view/index.ts b/packages/scaffold/src/views/w3m-convert-view/index.ts index c2ea27a6cc..6056285b62 100644 --- a/packages/scaffold/src/views/w3m-convert-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-view/index.ts @@ -132,7 +132,10 @@ export class W3mConvertView extends LitElement { // -- Private ------------------------------------------- // private watchConvertValues() { - this.gasFeeIntervalId = setInterval(() => ConvertController.refreshConvertValues(), 5000) + this.gasFeeIntervalId = setInterval(() => { + ConvertController.fetchTokens() + ConvertController.refreshConvertValues() + }, 5000) } private templateSwap() { diff --git a/packages/wallet/src/W3mFrame.ts b/packages/wallet/src/W3mFrame.ts index 2966114f4d..9ee4933230 100644 --- a/packages/wallet/src/W3mFrame.ts +++ b/packages/wallet/src/W3mFrame.ts @@ -38,6 +38,8 @@ export class W3mFrame { iframe.style.position = 'fixed' iframe.style.zIndex = '999999' iframe.style.display = 'none' + iframe.style.borderBottomLeftRadius = `clamp(0px, var(--wui-border-radius-l), 44px)` + iframe.style.borderBottomRightRadius = `clamp(0px, var(--wui-border-radius-l), 44px)` iframe.style.opacity = '0' iframe.style.borderBottomLeftRadius = `clamp(0px, var(--wui-border-radius-l), 44px)` iframe.style.borderBottomRightRadius = `clamp(0px, var(--wui-border-radius-l), 44px)` From 7a21054ab1108198fc5b23a5218782848e0a1bcb Mon Sep 17 00:00:00 2001 From: enesozturk Date: Fri, 5 Apr 2024 16:40:48 +0300 Subject: [PATCH 73/96] chore: update intervals --- .../w3m-convert-select-token-view/index.ts | 28 +++++++++++++------ .../src/views/w3m-convert-view/index.ts | 13 +++++---- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts b/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts index af4d666ec9..ec4faa8922 100644 --- a/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts @@ -15,6 +15,8 @@ export class W3mConvertSelectTokenView extends LitElement { private unsubscribe: ((() => void) | undefined)[] = [] // -- State & Properties -------------------------------- // + @state() private interval?: NodeJS.Timeout + @state() private targetToken = RouterController.state.data?.target @state() private sourceToken = ConvertController.state.sourceToken @@ -35,15 +37,8 @@ export class W3mConvertSelectTokenView extends LitElement { }) ] ) - } - private onSelectToken(token: TokenInfo) { - if (this.targetToken === 'sourceToken') { - ConvertController.setSourceToken(token) - } else { - ConvertController.setToToken(token) - } - RouterController.goBack() + this.watchTokens() } public override updated() { @@ -67,6 +62,7 @@ export class W3mConvertSelectTokenView extends LitElement { this.handleSuggestedTokensScroll.bind(this) ) tokensList?.removeEventListener('scroll', this.handleTokenListScroll.bind(this)) + clearInterval(this.interval) } // -- Render -------------------------------------------- // @@ -79,6 +75,22 @@ export class W3mConvertSelectTokenView extends LitElement { } // -- Private ------------------------------------------- // + private watchTokens() { + this.interval = setInterval(() => { + ConvertController.getNetworkTokenPrice() + ConvertController.getMyTokensWithBalance() + }, 5000) + } + + private onSelectToken(token: TokenInfo) { + if (this.targetToken === 'sourceToken') { + ConvertController.setSourceToken(token) + } else { + ConvertController.setToToken(token) + } + RouterController.goBack() + } + private templateSearchInput() { return html` diff --git a/packages/scaffold/src/views/w3m-convert-view/index.ts b/packages/scaffold/src/views/w3m-convert-view/index.ts index 6056285b62..4d7d481bb4 100644 --- a/packages/scaffold/src/views/w3m-convert-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-view/index.ts @@ -22,7 +22,7 @@ export class W3mConvertView extends LitElement { private unsubscribe: ((() => void) | undefined)[] = [] // -- State & Properties -------------------------------- // - @state() private gasFeeIntervalId?: NodeJS.Timeout + @state() private interval?: NodeJS.Timeout @state() private detailsOpen = false @@ -106,7 +106,7 @@ export class W3mConvertView extends LitElement { ] ) - this.watchConvertValues() + this.watchTokensAndValues() } public override firstUpdated() { @@ -118,7 +118,7 @@ export class W3mConvertView extends LitElement { public override disconnectedCallback() { ConvertController.setLoading(false) this.unsubscribe.forEach(unsubscribe => unsubscribe?.()) - clearInterval(this.gasFeeIntervalId) + clearInterval(this.interval) } // -- Render -------------------------------------------- // @@ -131,9 +131,10 @@ export class W3mConvertView extends LitElement { } // -- Private ------------------------------------------- // - private watchConvertValues() { - this.gasFeeIntervalId = setInterval(() => { - ConvertController.fetchTokens() + private watchTokensAndValues() { + this.interval = setInterval(() => { + ConvertController.getNetworkTokenPrice() + ConvertController.getMyTokensWithBalance() ConvertController.refreshConvertValues() }, 5000) } From b97d06973e3ed9137c03bd2ba6852d3beb513721 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Mon, 8 Apr 2024 12:53:51 +0300 Subject: [PATCH 74/96] chore: fix ui comp exports and conflicts --- packages/common/index.ts | 1 - packages/ui/src/components/wui-icon/index.ts | 2 -- 2 files changed, 3 deletions(-) diff --git a/packages/common/index.ts b/packages/common/index.ts index 2d0952a175..ea3d5cde2f 100644 --- a/packages/common/index.ts +++ b/packages/common/index.ts @@ -1,6 +1,5 @@ // -- Utils ------------------------------------------------------------------- export { DateUtil } from './src/utils/DateUtil.js' -export { NumberUtil } from './src/utils/NumberUtil.js' export { NetworkUtil } from './src/utils/NetworkUtil.js' export { NumberUtil } from './src/utils/NumberUtil.js' diff --git a/packages/ui/src/components/wui-icon/index.ts b/packages/ui/src/components/wui-icon/index.ts index 416cbf10f0..4b1e4d8e6e 100644 --- a/packages/ui/src/components/wui-icon/index.ts +++ b/packages/ui/src/components/wui-icon/index.ts @@ -89,7 +89,6 @@ const svgOptions: Record> = { checkmark: checkmarkSvg, checkmarkBold: checkmarkBoldSvg, chevronBottom: chevronBottomSvg, - checkmarkBold: checkmarkBoldSvg, chevronLeft: chevronLeftSvg, chevronRight: chevronRightSvg, chevronTop: chevronTopSvg, @@ -130,7 +129,6 @@ const svgOptions: Record> = { swapHorizontalBold: swapHorizontalBoldSvg, swapHorizontalRoundedBold: swapHorizontalRoundedBoldSvg, swapVertical: swapVerticalSvg, - swapHorizontalRoundedBold: swapHorizontalRoundedBoldSvg, telegram: telegramSvg, twitch: twitchSvg, twitter: twitterSvg, From e52483b369153d2ce490bdb0e083d6effbdff898 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Mon, 8 Apr 2024 13:01:56 +0300 Subject: [PATCH 75/96] chore: remove scaffold components from ui package --- packages/ui/index.ts | 2 - .../composites/wui-convert-details/index.ts | 129 -------- .../composites/wui-convert-details/styles.ts | 52 ---- .../src/composites/wui-convert-input/index.ts | 282 ------------------ .../composites/wui-convert-input/styles.ts | 129 -------- 5 files changed, 594 deletions(-) delete mode 100644 packages/ui/src/composites/wui-convert-details/index.ts delete mode 100644 packages/ui/src/composites/wui-convert-details/styles.ts delete mode 100644 packages/ui/src/composites/wui-convert-input/index.ts delete mode 100644 packages/ui/src/composites/wui-convert-input/styles.ts diff --git a/packages/ui/index.ts b/packages/ui/index.ts index c69eb64b93..a38fb8ff48 100644 --- a/packages/ui/index.ts +++ b/packages/ui/index.ts @@ -19,7 +19,6 @@ export * from './src/composites/wui-card-select-loader/index.js' export * from './src/composites/wui-card-select/index.js' export * from './src/composites/wui-chip/index.js' export * from './src/composites/wui-connect-button/index.js' -export * from './src/composites/wui-convert-details/index.js' export * from './src/composites/wui-cta-button/index.js' export * from './src/composites/wui-details-group/index.js' export * from './src/composites/wui-details-group-item/index.js' @@ -42,7 +41,6 @@ export * from './src/composites/wui-otp/index.js' export * from './src/composites/wui-qr-code/index.js' export * from './src/composites/wui-search-bar/index.js' export * from './src/composites/wui-snackbar/index.js' -export * from './src/composites/wui-convert-input/index.js' export * from './src/composites/wui-tabs/index.js' export * from './src/composites/wui-token-button/index.js' export * from './src/composites/wui-tag/index.js' diff --git a/packages/ui/src/composites/wui-convert-details/index.ts b/packages/ui/src/composites/wui-convert-details/index.ts deleted file mode 100644 index 7de2390202..0000000000 --- a/packages/ui/src/composites/wui-convert-details/index.ts +++ /dev/null @@ -1,129 +0,0 @@ -import { html, LitElement } from 'lit' -import { property } from 'lit/decorators.js' -import { customElement } from '../../utils/WebComponentsUtil.js' -import { resetStyles } from '../../utils/ThemeUtil.js' -import styles from './styles.js' -import { formatNumberToLocalString } from '../../utils/NumberUtil.js' - -@customElement('wui-convert-details') -export class WuiConvertDetails extends LitElement { - public static override styles = [resetStyles, styles] - - // -- State & Properties -------------------------------- // - @property() public detailsOpen = false - - @property() public sourceTokenSymbol?: number - - @property() public sourceTokenPrice?: number - - @property() public toTokenSymbol?: number - - @property() public toTokenConvertedAmount?: number - - @property() public gasPriceInUSD?: number - - @property() public priceImpact?: number - - @property() public slippageRate = 0.5 - - @property() public maxSlippage?: number - - // -- Render -------------------------------------------- // - public override render() { - return html` - - - - ${this.detailsOpen - ? html` - - - - Network cost - - $${formatNumberToLocalString(this.gasPriceInUSD, 3)} - - - - ${this.priceImpact - ? html` - - Price impact - - - ${formatNumberToLocalString(this.priceImpact, 3)}% - - - - ` - : null} - ${this.maxSlippage && this.sourceTokenSymbol - ? html` - - Max. slippage - - - ${formatNumberToLocalString(this.maxSlippage, 6)} - ${this.sourceTokenSymbol} ${this.slippageRate}% - - - - ` - : null} - - - Provider fee - - Free - - - - - ` - : null} - - - ` - } - - // -- Private ------------------------------------------- // - private toggleDetails() { - this.detailsOpen = !this.detailsOpen - } -} - -declare global { - interface HTMLElementTagNameMap { - 'wui-convert-details': WuiConvertDetails - } -} diff --git a/packages/ui/src/composites/wui-convert-details/styles.ts b/packages/ui/src/composites/wui-convert-details/styles.ts deleted file mode 100644 index b6abae5746..0000000000 --- a/packages/ui/src/composites/wui-convert-details/styles.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { css } from 'lit' - -export default css` - :host { - width: 100%; - } - - .details-container > wui-flex { - background: var(--wui-gray-glass-002); - border-radius: var(--wui-border-radius-xxs); - width: 100%; - } - - .details-container > wui-flex > button { - border: none; - background: none; - padding: var(--wui-spacing-s); - border-radius: var(--wui-border-radius-xxs); - cursor: pointer; - } - - .details-content-container { - padding: var(--wui-spacing-1xs); - padding-top: 0px; - display: flex; - align-items: center; - justify-content: center; - } - - .details-content-container > wui-flex { - width: 100%; - } - - .details-row { - width: 100%; - padding: var(--wui-spacing-s); - padding-left: var(--wui-spacing-s); - padding-right: var(--wui-spacing-1xs); - border-radius: calc(var(--wui-border-radius-5xs) + var(--wui-border-radius-4xs)); - background: var(--wui-gray-glass-002); - } - - .details-row.provider-free-row { - padding-right: var(--wui-spacing-xs); - } - - .free-badge { - background: rgba(38, 217, 98, 0.15); - border-radius: var(--wui-border-radius-4xs); - padding: 4.5px 6px; - } -` diff --git a/packages/ui/src/composites/wui-convert-input/index.ts b/packages/ui/src/composites/wui-convert-input/index.ts deleted file mode 100644 index 1fbf80edab..0000000000 --- a/packages/ui/src/composites/wui-convert-input/index.ts +++ /dev/null @@ -1,282 +0,0 @@ -import { html, LitElement } from 'lit' -import { property } from 'lit/decorators.js' -import { customElement } from '../../utils/WebComponentsUtil.js' -import { resetStyles } from '../../utils/ThemeUtil.js' -import '../../components/wui-text/index.js' -import '../wui-transaction-visual/index.js' -import { EventsController, RouterController } from '@web3modal/core' -import styles from './styles.js' -import { formatNumberToLocalString } from '../../utils/NumberUtil.js' -import { NumberUtil } from '@web3modal/common' - -const MINIMUM_USD_VALUE_TO_CONVERT = 0.00005 - -type Target = 'sourceToken' | 'toToken' - -interface TokenInfo { - address: `0x${string}` - symbol: string - name: string - decimals: number - logoURI: string - domainVersion?: string - eip2612?: boolean - isFoT?: boolean - tags?: string[] -} - -@customElement('wui-convert-input') -export class WuiConvertInput extends LitElement { - public static override styles = [resetStyles, styles] - - // -- State & Properties -------------------------------- // - @property() public focused = false - - @property() public balance: string | undefined - - @property() public value?: string - - @property() public price = 0 - - @property() public marketValue?: string = '$1.0345,00' - - @property() public disabled?: boolean - - @property() public target: Target = 'sourceToken' - - @property() public token?: TokenInfo - - @property() public onSetAmount: ((target: Target, value: string) => void) | null = null - - @property() public onSetMaxValue: ((target: Target, balance: string | undefined) => void) | null = - null - - // -- Render -------------------------------------------- // - public override render() { - const marketValue = this.marketValue || '0' - const isMarketValueGreaterThanZero = NumberUtil.bigNumber(marketValue).isGreaterThan(0) - - return html` - - ${this.target === 'sourceToken' - ? this.templateSourceInputBackgroundMask() - : this.templateToInputBackgroundMask()} - - this.onFocusChange(true)} - @focusout=${() => this.onFocusChange(false)} - ?disabled=${this.disabled} - .value=${this.value} - @input=${this.dispatchInputChangeEvent} - @keydown=${this.handleKeydown} - placeholder="0" - /> - - ${isMarketValueGreaterThanZero ? `$${this.marketValue}` : null} - - - ${this.templateTokenSelectButton()} - - ` - } - - // -- Private ------------------------------------------- // - private handleKeydown(event: KeyboardEvent) { - const allowedKeys = [ - 'Backspace', - 'Meta', - 'Ctrl', - 'a', - 'c', - 'v', - 'ArrowLeft', - 'ArrowRight', - 'Tab' - ] - const isComma = event.key === ',' - const isDot = event.key === '.' - const isNumericKey = event.key >= '0' && event.key <= '9' - const currentValue = this.value - - if (!isNumericKey && !allowedKeys.includes(event.key) && !isDot && !isComma) { - event.preventDefault() - } - - if (isComma || isDot) { - if (currentValue?.includes('.') || currentValue?.includes(',')) { - event.preventDefault() - } - } - } - - private dispatchInputChangeEvent(event: InputEvent) { - if (!this.onSetAmount) { - return - } - - const value = (event.target as HTMLInputElement).value - if (value === ',' || value === '.') { - this.onSetAmount(this.target, '0.') - } else if (value.endsWith(',')) { - this.onSetAmount(this.target, value.replace(',', '.')) - } else { - this.onSetAmount(this.target, value) - } - } - - private setMaxValueToInput() { - this.onSetMaxValue?.(this.target, this.balance) - } - - private templateTokenSelectButton() { - if (!this.token) { - return html` - Select token - ` - } - - const tokenElement = this.token.logoURI - ? html`` - : html` - - ` - - return html` - - - ${this.tokenBalanceTemplate()} - - ` - } - - private tokenBalanceTemplate() { - const balanceValueInUSD = NumberUtil.multiply(this.balance, this.price) - const haveBalance = balanceValueInUSD - ? balanceValueInUSD?.isGreaterThan(MINIMUM_USD_VALUE_TO_CONVERT) - : false - - return html` - ${haveBalance - ? html` - ${formatNumberToLocalString(this.balance, 3)} - ` - : null} - ${this.target === 'sourceToken' ? this.tokenActionButtonTemplate(haveBalance) : null} - ` - } - - private templateSourceInputBackgroundMask() { - return html` - - - - - - - - ` - } - - private templateToInputBackgroundMask() { - return html` - - - - - - - - ` - } - - private tokenActionButtonTemplate(_haveBalance: boolean) { - if (_haveBalance) { - return html` ` - } - - return html` ` - } - - private onFocusChange(state: boolean) { - this.focused = state - } - - private onSelectToken() { - EventsController.sendEvent({ type: 'track', event: 'CLICK_SELECT_TOKEN_TO_SWAP' }) - RouterController.push('ConvertSelectToken', { - target: this.target - }) - } - - private onBuyToken() { - RouterController.push('OnRampProviders') - } -} - -declare global { - interface HTMLElementTagNameMap { - 'wui-convert-input': WuiConvertInput - } -} diff --git a/packages/ui/src/composites/wui-convert-input/styles.ts b/packages/ui/src/composites/wui-convert-input/styles.ts deleted file mode 100644 index a9773efbb0..0000000000 --- a/packages/ui/src/composites/wui-convert-input/styles.ts +++ /dev/null @@ -1,129 +0,0 @@ -import { css } from 'lit' - -export default css` - :host > wui-flex { - display: flex; - flex-direction: row; - justify-content: space-between; - align-items: center; - border-radius: var(--wui-border-radius-s); - padding: var(--wui-spacing-xl); - padding-right: var(--wui-spacing-s); - width: 100%; - height: 100px; - box-sizing: border-box; - position: relative; - } - - :host > wui-flex > svg.input_mask { - position: absolute; - inset: 0; - z-index: 5; - } - - :host wui-flex .input_mask__border, - :host wui-flex .input_mask__background { - transition: fill var(--wui-duration-md) var(--wui-ease-out-power-1); - will-change: fill; - } - - :host wui-flex .input_mask__border { - fill: var(--wui-gray-glass-005); - } - - :host wui-flex .input_mask__background { - fill: var(--wui-gray-glass-002); - } - - :host wui-flex.focus .input_mask__border { - fill: var(--wui-gray-glass-020); - } - - :host > wui-flex .swap-input, - :host > wui-flex .swap-token-button { - z-index: 10; - } - - :host > wui-flex .swap-input { - -webkit-mask-image: linear-gradient( - 270deg, - transparent 0px, - transparent 8px, - black 24px, - black 25px, - black 32px, - black 100% - ); - mask-image: linear-gradient( - 270deg, - transparent 0px, - transparent 8px, - black 24px, - black 25px, - black 32px, - black 100% - ); - } - - :host > wui-flex .swap-input input { - background: none; - border: none; - height: 42px; - width: 100%; - font-size: 32px; - font-style: normal; - font-weight: 400; - line-height: 130%; - letter-spacing: -1.28px; - outline: none; - caret-color: var(--wui-color-accent-100); - } - - :host > wui-flex .swap-input input:focus-visible { - outline: none; - } - - :host > wui-flex .swap-input input::-webkit-outer-spin-button, - :host > wui-flex .swap-input input::-webkit-inner-spin-button { - -webkit-appearance: none; - margin: 0; - } - - .token-select-button { - display: flex; - align-items: center; - justify-content: center; - gap: var(--wui-spacing-xxs); - padding: var(--wui-spacing-xs); - padding-right: var(--wui-spacing-1xs); - height: 40px; - border: none; - border-radius: 80px; - background: var(--wui-gray-glass-002); - box-shadow: inset 0 0 0 1px var(--wui-gray-glass-002); - cursor: pointer; - transition: background 0.2s linear; - } - - .token-select-button:hover { - background: var(--wui-gray-glass-005); - } - - .token-select-button wui-image { - width: 24px; - height: 24px; - border-radius: var(--wui-border-radius-s); - box-shadow: inset 0 0 0 1px var(--wui-gray-glass-010); - } - - .max-value-button { - background-color: transparent; - border: none; - cursor: pointer; - color: var(--wui-gray-glass-020); - } - - .market-value { - min-height: 18px; - } -` From d6a7900cf7570d9ea687be0baa75335316bd74b0 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Mon, 8 Apr 2024 14:30:10 +0300 Subject: [PATCH 76/96] fix: convert components and styles --- packages/scaffold/src/partials/w3m-convert-input/index.ts | 3 +++ .../scaffold/src/views/w3m-convert-preview-view/index.ts | 4 ++-- packages/scaffold/src/views/w3m-convert-view/index.ts | 8 ++++---- packages/ui/src/composites/wui-token-button/styles.ts | 2 +- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/scaffold/src/partials/w3m-convert-input/index.ts b/packages/scaffold/src/partials/w3m-convert-input/index.ts index 2014d5e417..74032ab3a0 100644 --- a/packages/scaffold/src/partials/w3m-convert-input/index.ts +++ b/packages/scaffold/src/partials/w3m-convert-input/index.ts @@ -212,6 +212,9 @@ export class W3mConvertInput extends LitElement { private onSelectToken() { EventsController.sendEvent({ type: 'track', event: 'CLICK_SELECT_TOKEN_TO_SWAP' }) + RouterController.push('ConvertSelectToken', { + target: this.target + }) } private onBuyToken() { diff --git a/packages/scaffold/src/views/w3m-convert-preview-view/index.ts b/packages/scaffold/src/views/w3m-convert-preview-view/index.ts index ef6775d60e..61de79d57b 100644 --- a/packages/scaffold/src/views/w3m-convert-preview-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-preview-view/index.ts @@ -186,7 +186,7 @@ export class W3mConvertPreviewView extends LitElement { : 0 return html` - + > ` } diff --git a/packages/scaffold/src/views/w3m-convert-view/index.ts b/packages/scaffold/src/views/w3m-convert-view/index.ts index 4d7d481bb4..a68a8ad4b9 100644 --- a/packages/scaffold/src/views/w3m-convert-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-view/index.ts @@ -205,7 +205,7 @@ export class W3mConvertView extends LitElement { value -= this.gasPriceInUSD || 0 } - return html`` + >` } private onSetMaxValue(target: Target, balance: string | undefined) { @@ -263,7 +263,7 @@ export class W3mConvertView extends LitElement { : 0 return html` - + > ` } diff --git a/packages/ui/src/composites/wui-token-button/styles.ts b/packages/ui/src/composites/wui-token-button/styles.ts index 702b8032c2..cb252eff47 100644 --- a/packages/ui/src/composites/wui-token-button/styles.ts +++ b/packages/ui/src/composites/wui-token-button/styles.ts @@ -10,7 +10,7 @@ export default css` padding: var(--wui-spacing-xs); padding-right: var(--wui-spacing-1xs); height: 40px; - border-width: 0px; + border-radius: var(--wui-border-radius-3xl); background: var(--wui-gray-glass-002); border-width: 0px; box-shadow: inset 0 0 0 1px var(--wui-gray-glass-002); From 18baf26c2a7c6231e7529a0a1682afdb6068ff53 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Mon, 8 Apr 2024 19:31:24 +0300 Subject: [PATCH 77/96] feat: add unit tests for convert, fix router controller tests --- .../core/src/controllers/ConvertController.ts | 66 ++++++++++----- .../controllers/ConvertController.test.ts | 81 +++++++++++++++++-- .../controllers/RouterController.test.ts | 24 ++++-- .../core/tests/mocks/ConvertController.ts | 45 +++++++++++ .../src/partials/w3m-convert-input/styles.ts | 1 + 5 files changed, 183 insertions(+), 34 deletions(-) create mode 100644 packages/core/tests/mocks/ConvertController.ts diff --git a/packages/core/src/controllers/ConvertController.ts b/packages/core/src/controllers/ConvertController.ts index b56052fba3..8a14dd5752 100644 --- a/packages/core/src/controllers/ConvertController.ts +++ b/packages/core/src/controllers/ConvertController.ts @@ -8,7 +8,7 @@ import { SnackController } from './SnackController.js' import { RouterController } from './RouterController.js' import { NumberUtil } from '@web3modal/common' -const INITIAL_GAS_LIMIT = 150000 +export const INITIAL_GAS_LIMIT = 150000 // -- Types --------------------------------------------- // type TransactionParams = { @@ -167,7 +167,7 @@ export const ConvertController = { }, setLoading(loading: boolean) { - ConvertController.state.loading = loading + state.loading = loading }, setSourceToken(sourceToken: TokenInfo | undefined) { @@ -215,7 +215,11 @@ export const ConvertController = { }, async setTokenValues(address: string, target: 'sourceToken' | 'toToken') { - const price = await this.getAddressPrice(address) + let price = parseFloat(state.tokensPriceMap[address] || '0') + + if (!price) { + price = await this.getAddressPrice(address) + } if (target === 'sourceToken') { state.sourceTokenPriceInUSD = price @@ -628,32 +632,56 @@ export const ConvertController = { } }, - setTransactionDetails(transaction: TransactionParams | null) { - const { sourceTokenAddress, toTokenAddress, toTokenDecimals } = this.getParams() + getToTokenValues(amountBigInt: string, decimals: number) { + const { toTokenAddress } = this.getParams() - if (!transaction || !toTokenAddress || !toTokenDecimals) { - return + if (!toTokenAddress) { + return { + toTokenAmount: '0', + toTokenPriceInUSD: 0 + } } - const toTokenPrice = state.tokensPriceMap[toTokenAddress] || '0' - state.toTokenAmount = NumberUtil.bigNumber(transaction.toAmount) - .dividedBy(10 ** toTokenDecimals) + const toTokenAmount = NumberUtil.bigNumber(amountBigInt) + .dividedBy(10 ** decimals) .toFixed(20) - state.toTokenPriceInUSD = NumberUtil.bigNumber(toTokenPrice).toNumber() - state.gasPriceInUSD = this.calculateGasPriceInUSD(transaction.gas, transaction.gasPrice) + const toTokenPrice = state.tokensPriceMap[toTokenAddress] || '0' + const toTokenPriceInUSD = NumberUtil.bigNumber(toTokenPrice).toNumber() - const isSourceTokenIsNetworkToken = sourceTokenAddress === ConstantsUtil.NATIVE_TOKEN_ADDRESS - const totalNativeTokenCostInUSD = isSourceTokenIsNetworkToken - ? NumberUtil.bigNumber(state.sourceTokenPriceInUSD).plus(state.gasPriceInUSD) - : state.gasPriceInUSD - const insufficientBalance = NumberUtil.bigNumber(totalNativeTokenCostInUSD).isGreaterThan( + return { + toTokenAmount, + toTokenPriceInUSD + } + }, + + isInsufficientNetworkTokenForGas() { + return NumberUtil.bigNumber(NumberUtil.bigNumber(state.gasPriceInUSD || '0')).isGreaterThan( state.networkBalanceInUSD ) + }, + + setTransactionDetails(transaction: TransactionParams | null) { + const { toTokenAddress, toTokenDecimals } = this.getParams() + + if (!transaction || !toTokenAddress || !toTokenDecimals) { + return + } + + const insufficientNetworkToken = this.isInsufficientNetworkTokenForGas() - if (insufficientBalance) { - state.inputError = insufficientBalance ? 'Insufficient balance' : undefined + if (insufficientNetworkToken) { + state.inputError = 'Insufficient balance' + } else { + state.inputError = undefined } + const { toTokenAmount, toTokenPriceInUSD } = this.getToTokenValues( + transaction.toAmount, + toTokenDecimals + ) + state.toTokenAmount = toTokenAmount + state.toTokenPriceInUSD = toTokenPriceInUSD + state.gasPriceInUSD = this.calculateGasPriceInUSD(transaction.gas, transaction.gasPrice) state.priceImpact = this.calculatePriceImpact(state.toTokenAmount, state.gasPriceInUSD) state.maxSlippage = this.calculateMaxSlippage() } diff --git a/packages/core/tests/controllers/ConvertController.test.ts b/packages/core/tests/controllers/ConvertController.test.ts index 52c6841da5..029522e40f 100644 --- a/packages/core/tests/controllers/ConvertController.test.ts +++ b/packages/core/tests/controllers/ConvertController.test.ts @@ -1,12 +1,79 @@ -import { describe, expect, it } from 'vitest' -import { ConvertController } from '../../index.js' +import { beforeAll, describe, expect, it } from 'vitest' +import { AccountController, ConvertController } from '../../index.js' +import { prices, tokenInfo } from '../mocks/ConvertController.js' +import type { TokenInfo } from '../../src/utils/ConvertApiUtil.js' +import { INITIAL_GAS_LIMIT } from '../../src/controllers/ConvertController.js' + +// - Mocks --------------------------------------------------------------------- +const caipAddress = 'eip155:1:0x123' +const gasLimit = BigInt(INITIAL_GAS_LIMIT) +const gasFee = BigInt(455966887160) + +const sourceTokenAddress = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee' +const toTokenAddress = '0xb33eaad8d922b1083446dc23f610c2567fb5180f' + +const sourceToken = tokenInfo[sourceTokenAddress] as TokenInfo +const toToken = tokenInfo[toTokenAddress] as TokenInfo + +// - Helpers +function setSourceTokenAmount(value: string) { + ConvertController.setSourceTokenAmount(value) + const toTokenAmount = ConvertController.getToAmount() + const toTokenValues = ConvertController.getToTokenValues(toTokenAmount, toToken.decimals) + ConvertController.state.toTokenAmount = toTokenValues.toTokenAmount + ConvertController.state.toTokenPriceInUSD = toTokenValues.toTokenPriceInUSD +} + +// - Setup --------------------------------------------------------------------- +beforeAll(() => { + AccountController.setCaipAddress(caipAddress) + ConvertController.state.tokensPriceMap = prices + ConvertController.state.networkPrice = prices[sourceTokenAddress] + ConvertController.state.networkBalanceInUSD = '2' + ConvertController.state.gasPriceInUSD = ConvertController.calculateGasPriceInUSD(gasLimit, gasFee) + ConvertController.setSourceToken(sourceToken) + ConvertController.setToToken(toToken) + setSourceTokenAmount('1') +}) // -- Tests -------------------------------------------------------------------- describe('ConvertController', () => { - it('should have default state as expected', () => { - expect(ConvertController.state.initialized).toEqual(false) - expect(ConvertController.state.tokens).toEqual([]) - expect(ConvertController.state.sourceToken).toEqual(undefined) - expect(ConvertController.state.toToken).toEqual(undefined) + it('should set toToken as expected', () => { + expect(ConvertController.state.toToken?.address).toEqual(toToken.address) + }) + + it('should set sourceToken as expected', () => { + expect(ConvertController.state.sourceToken?.address).toEqual(sourceToken.address) + }) + + it('should calculate gas price in Ether and USD as expected', async () => { + const gasPriceInEther = ConvertController.calculateGasPriceInEther(gasLimit, gasFee) + const gasPriceInUSD = ConvertController.calculateGasPriceInUSD(gasLimit, gasFee) + + expect(gasPriceInEther).toEqual(0.068395033074) + expect(gasPriceInUSD).toEqual(0.06395499714651795) + }) + + it('should return insufficient balance as expected', async () => { + ConvertController.state.networkBalanceInUSD = '0' + expect(ConvertController.isInsufficientNetworkTokenForGas()).toEqual(true) + }) + + it('should calculate convert values as expected', async () => { + expect(ConvertController.state.toTokenAmount).toEqual('0.07942958313582482619') + expect(ConvertController.state.toTokenPriceInUSD).toEqual(11.772471201328177) + }) + + it('should calculate the price impact as expected', async () => { + const priceImpact = ConvertController.calculatePriceImpact( + ConvertController.state.toTokenAmount, + ConvertController.calculateGasPriceInUSD(gasLimit, gasFee) + ) + expect(priceImpact).equal(6.839503307400001) + }) + + it('should calculate the maximum slippage as expected', async () => { + const maxSlippage = ConvertController.calculateMaxSlippage() + expect(maxSlippage).toEqual(0.005) }) }) diff --git a/packages/core/tests/controllers/RouterController.test.ts b/packages/core/tests/controllers/RouterController.test.ts index 65f676e869..72ac54ff24 100644 --- a/packages/core/tests/controllers/RouterController.test.ts +++ b/packages/core/tests/controllers/RouterController.test.ts @@ -6,7 +6,8 @@ describe('RouterController', () => { it('should have valid default state', () => { expect(RouterController.state).toEqual({ view: 'Connect', - history: ['Connect'] + history: ['Connect'], + transactionStack: [] }) }) @@ -14,7 +15,8 @@ describe('RouterController', () => { RouterController.push('Account') expect(RouterController.state).toEqual({ view: 'Account', - history: ['Connect', 'Account'] + history: ['Connect', 'Account'], + transactionStack: [] }) }) @@ -22,7 +24,8 @@ describe('RouterController', () => { RouterController.push('Account') expect(RouterController.state).toEqual({ view: 'Account', - history: ['Connect', 'Account'] + history: ['Connect', 'Account'], + transactionStack: [] }) }) @@ -30,7 +33,8 @@ describe('RouterController', () => { RouterController.goBack() expect(RouterController.state).toEqual({ view: 'Connect', - history: ['Connect'] + history: ['Connect'], + transactionStack: [] }) }) @@ -38,7 +42,8 @@ describe('RouterController', () => { RouterController.goBack() expect(RouterController.state).toEqual({ view: 'Connect', - history: ['Connect'] + history: ['Connect'], + transactionStack: [] }) }) @@ -46,7 +51,8 @@ describe('RouterController', () => { RouterController.reset('Account') expect(RouterController.state).toEqual({ view: 'Account', - history: ['Account'] + history: ['Account'], + transactionStack: [] }) }) @@ -55,7 +61,8 @@ describe('RouterController', () => { RouterController.replace('Networks') expect(RouterController.state).toEqual({ view: 'Networks', - history: ['Account', 'Networks'] + history: ['Account', 'Networks'], + transactionStack: [] }) }) @@ -68,7 +75,8 @@ describe('RouterController', () => { history: ['Account', 'Networks', 'ConnectingExternal'], data: { connector: { id: 'test', type: 'WALLET_CONNECT' } - } + }, + transactionStack: [] }) }) }) diff --git a/packages/core/tests/mocks/ConvertController.ts b/packages/core/tests/mocks/ConvertController.ts new file mode 100644 index 0000000000..6622cbb5db --- /dev/null +++ b/packages/core/tests/mocks/ConvertController.ts @@ -0,0 +1,45 @@ +export const tokenInfo = { + '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee': { + address: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee', + chainId: 137, + decimals: 18, + name: 'MATIC', + symbol: 'MATIC', + providers: ['1inch', 'Curve Token List'], + logoURI: 'https://tokens.1inch.io/0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0.png', + tags: ['native'] + }, + '0xb33eaad8d922b1083446dc23f610c2567fb5180f': { + address: '0xb33eaad8d922b1083446dc23f610c2567fb5180f', + chainId: 137, + decimals: 18, + name: 'Uniswap', + symbol: 'UNI', + providers: [ + '1inch', + 'CoinGecko', + 'Kleros Tokens', + 'Quickswap Token List', + 'Trust Wallet Assets', + 'Uniswap Labs Default' + ], + logoURI: 'https://tokens.1inch.io/0x1f9840a85d5af5bf1d1762f925bdaddc4201f984.png', + tags: ['tokens'] + } +} + +export const balance = { + '0x65a05db8322701724c197af82c9cae41195b0aa8': '9348572710146769370', + '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee': '4960248983278245480', + '0xb33eaad8d922b1083446dc23f610c2567fb5180f': '564297973603949455', + '0xc2132d05d31c914a87c6611c10748aeb04b58e8f': '888765', + '0x2c89bbc92bd86f8075d1decc58c7f4e0107f286b': '219869721034228067' +} + +export const prices = { + '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee': '0.93508248', + '0x65a05db8322701724c197af82c9cae41195b0aa8': '0.10315220553291868', + '0xb33eaad8d922b1083446dc23f610c2567fb5180f': '11.772471201328177', + '0xc2132d05d31c914a87c6611c10748aeb04b58e8f': '0.9995840116762155', + '0x2c89bbc92bd86f8075d1decc58c7f4e0107f286b': '52.09209274652567' +} diff --git a/packages/scaffold/src/partials/w3m-convert-input/styles.ts b/packages/scaffold/src/partials/w3m-convert-input/styles.ts index fab1f1f269..0006942093 100644 --- a/packages/scaffold/src/partials/w3m-convert-input/styles.ts +++ b/packages/scaffold/src/partials/w3m-convert-input/styles.ts @@ -122,6 +122,7 @@ export default css` border: none; cursor: pointer; color: var(--wui-gray-glass-020); + padding-left: 0px; } .market-value { From ca72aede65f50f9cb76b6de54c204ce38413d636 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Mon, 8 Apr 2024 19:32:43 +0300 Subject: [PATCH 78/96] chore: linter issues --- .../core/tests/controllers/ConvertController.test.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/core/tests/controllers/ConvertController.test.ts b/packages/core/tests/controllers/ConvertController.test.ts index 029522e40f..f34c515f6b 100644 --- a/packages/core/tests/controllers/ConvertController.test.ts +++ b/packages/core/tests/controllers/ConvertController.test.ts @@ -46,7 +46,7 @@ describe('ConvertController', () => { expect(ConvertController.state.sourceToken?.address).toEqual(sourceToken.address) }) - it('should calculate gas price in Ether and USD as expected', async () => { + it('should calculate gas price in Ether and USD as expected', () => { const gasPriceInEther = ConvertController.calculateGasPriceInEther(gasLimit, gasFee) const gasPriceInUSD = ConvertController.calculateGasPriceInUSD(gasLimit, gasFee) @@ -54,17 +54,17 @@ describe('ConvertController', () => { expect(gasPriceInUSD).toEqual(0.06395499714651795) }) - it('should return insufficient balance as expected', async () => { + it('should return insufficient balance as expected', () => { ConvertController.state.networkBalanceInUSD = '0' expect(ConvertController.isInsufficientNetworkTokenForGas()).toEqual(true) }) - it('should calculate convert values as expected', async () => { + it('should calculate convert values as expected', () => { expect(ConvertController.state.toTokenAmount).toEqual('0.07942958313582482619') expect(ConvertController.state.toTokenPriceInUSD).toEqual(11.772471201328177) }) - it('should calculate the price impact as expected', async () => { + it('should calculate the price impact as expected', () => { const priceImpact = ConvertController.calculatePriceImpact( ConvertController.state.toTokenAmount, ConvertController.calculateGasPriceInUSD(gasLimit, gasFee) @@ -72,7 +72,7 @@ describe('ConvertController', () => { expect(priceImpact).equal(6.839503307400001) }) - it('should calculate the maximum slippage as expected', async () => { + it('should calculate the maximum slippage as expected', () => { const maxSlippage = ConvertController.calculateMaxSlippage() expect(maxSlippage).toEqual(0.005) }) From 43c29c6a725b019f8e971f27101bdeb17b0fead0 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Mon, 15 Apr 2024 12:02:55 +0300 Subject: [PATCH 79/96] chore: remove swap from default w3m and remove toast on cancel transaction --- packages/core/src/controllers/ConvertController.ts | 1 - .../partials/w3m-account-default-widget/index.ts | 14 -------------- 2 files changed, 15 deletions(-) diff --git a/packages/core/src/controllers/ConvertController.ts b/packages/core/src/controllers/ConvertController.ts index 8a14dd5752..279f31058b 100644 --- a/packages/core/src/controllers/ConvertController.ts +++ b/packages/core/src/controllers/ConvertController.ts @@ -542,7 +542,6 @@ export const ConvertController = { const error = err as TransactionError state.transactionError = error?.shortMessage as unknown as string state.transactionLoading = false - SnackController.showError(error?.shortMessage || 'Transaction error') } }, diff --git a/packages/scaffold/src/partials/w3m-account-default-widget/index.ts b/packages/scaffold/src/partials/w3m-account-default-widget/index.ts index 05beb73d4d..8703f0af4b 100644 --- a/packages/scaffold/src/partials/w3m-account-default-widget/index.ts +++ b/packages/scaffold/src/partials/w3m-account-default-widget/index.ts @@ -145,15 +145,6 @@ export class W3mAccountDefaultWidget extends LitElement { > Activity - - Convert - Date: Mon, 15 Apr 2024 19:37:49 +0300 Subject: [PATCH 80/96] refactor: convert input text color --- packages/scaffold/src/partials/w3m-convert-input/styles.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scaffold/src/partials/w3m-convert-input/styles.ts b/packages/scaffold/src/partials/w3m-convert-input/styles.ts index 0006942093..43a4782e4a 100644 --- a/packages/scaffold/src/partials/w3m-convert-input/styles.ts +++ b/packages/scaffold/src/partials/w3m-convert-input/styles.ts @@ -77,7 +77,7 @@ export default css` letter-spacing: -1.28px; outline: none; caret-color: var(--wui-color-accent-100); - color: var(--wui-color-fg-200); + color: var(--wui-color-fg-100); } :host > wui-flex .swap-input input:focus-visible { From aa9374daa3040e0f3aa788f0d55439c84f5f7c1a Mon Sep 17 00:00:00 2001 From: enesozturk Date: Mon, 15 Apr 2024 19:38:53 +0300 Subject: [PATCH 81/96] chore: switch to blockchain api for convert tokens and balance --- packages/common/src/utils/TypeUtil.ts | 1 + .../controllers/BlockchainApiController.ts | 13 ++- .../core/src/controllers/ConvertController.ts | 98 ++++++++++--------- packages/core/src/utils/ConvertApiUtil.ts | 82 +++++++++------- packages/core/src/utils/TypeUtil.ts | 18 ++++ .../src/partials/w3m-convert-input/index.ts | 20 +--- .../views/w3m-convert-preview-view/index.ts | 4 +- .../w3m-convert-select-token-view/index.ts | 61 ++++++------ .../src/views/w3m-convert-view/index.ts | 12 ++- 9 files changed, 172 insertions(+), 137 deletions(-) diff --git a/packages/common/src/utils/TypeUtil.ts b/packages/common/src/utils/TypeUtil.ts index c45dd69c6f..832d148adb 100644 --- a/packages/common/src/utils/TypeUtil.ts +++ b/packages/common/src/utils/TypeUtil.ts @@ -77,6 +77,7 @@ export interface Balance { name: string symbol: string chainId: string + address: string value?: number price: number quantity: BalanceQuantity diff --git a/packages/core/src/controllers/BlockchainApiController.ts b/packages/core/src/controllers/BlockchainApiController.ts index a75641b6f5..726b8b9b5b 100644 --- a/packages/core/src/controllers/BlockchainApiController.ts +++ b/packages/core/src/controllers/BlockchainApiController.ts @@ -3,6 +3,8 @@ import { FetchUtil } from '../utils/FetchUtil.js' import type { BlockchainApiTransactionsRequest, BlockchainApiTransactionsResponse, + BlockchainApiConvertTokensRequest, + BlockchainApiConvertTokensResponse, BlockchainApiIdentityRequest, BlockchainApiIdentityResponse, GenerateOnRampUrlArgs, @@ -123,12 +125,19 @@ export const BlockchainApiController = { }) }, - async getBalance(address: string) { + fetchConvertTokens({ projectId, chainId }: BlockchainApiConvertTokensRequest) { + return api.get({ + path: `/v1/convert/tokens?projectId=${projectId}&chainId=${chainId}` + }) + }, + + async getBalance(address: string, chainId?: string) { return api.get({ path: `/v1/account/${address}/balance`, params: { currency: 'usd', - projectId: OptionsController.state.projectId + projectId: OptionsController.state.projectId, + chainId } }) }, diff --git a/packages/core/src/controllers/ConvertController.ts b/packages/core/src/controllers/ConvertController.ts index 279f31058b..ca1ac06770 100644 --- a/packages/core/src/controllers/ConvertController.ts +++ b/packages/core/src/controllers/ConvertController.ts @@ -7,6 +7,7 @@ import { ConvertApiUtil } from '../utils/ConvertApiUtil.js' import { SnackController } from './SnackController.js' import { RouterController } from './RouterController.js' import { NumberUtil } from '@web3modal/common' +import type { ConvertToken } from '../utils/TypeUtil.js' export const INITIAL_GAS_LIMIT = 150000 @@ -20,6 +21,15 @@ type TransactionParams = { toAmount: string } +export type ConvertTokenWithBalance = ConvertToken & { + quantity?: { + numeric: string + decimals: string + } + price?: number + value?: number +} + class TransactionError extends Error { shortMessage?: string @@ -43,10 +53,10 @@ export interface ConvertControllerState { transactionError?: string // Input values - sourceToken?: TokenInfo + sourceToken?: ConvertTokenWithBalance sourceTokenAmount: string sourceTokenPriceInUSD: number - toToken?: TokenInfo + toToken?: ConvertTokenWithBalance toTokenAmount: string toTokenPriceInUSD: number networkPrice: string @@ -57,12 +67,12 @@ export interface ConvertControllerState { slippage: string // Tokens - tokens?: Record - suggestedTokens?: Record - popularTokens?: Record - foundTokens?: TokenInfo[] - myTokensWithBalance?: Record - tokensPriceMap: Record + tokens?: ConvertTokenWithBalance[] + suggestedTokens?: ConvertTokenWithBalance[] + popularTokens?: ConvertTokenWithBalance[] + foundTokens?: ConvertTokenWithBalance[] + myTokensWithBalance?: ConvertTokenWithBalance[] + tokensPriceMap: Record // Calculations gasFee: bigint @@ -170,7 +180,7 @@ export const ConvertController = { state.loading = loading }, - setSourceToken(sourceToken: TokenInfo | undefined) { + setSourceToken(sourceToken: ConvertTokenWithBalance | undefined) { if (!sourceToken) { return } @@ -189,7 +199,7 @@ export const ConvertController = { } }, - setToToken(toToken: TokenInfo | undefined) { + setToToken(toToken: ConvertTokenWithBalance | undefined) { const { sourceTokenAddress, sourceTokenAmount } = this.getParams() if (!toToken) { @@ -215,7 +225,7 @@ export const ConvertController = { }, async setTokenValues(address: string, target: 'sourceToken' | 'toToken') { - let price = parseFloat(state.tokensPriceMap[address] || '0') + let price = state.tokensPriceMap[address] || 0 if (!price) { price = await this.getAddressPrice(address) @@ -247,7 +257,9 @@ export const ConvertController = { }, resetValues() { - const networkToken = state.tokens?.[ConstantsUtil.NATIVE_TOKEN_ADDRESS] + const networkToken = state.tokens?.find( + token => token.address === ConstantsUtil.NATIVE_TOKEN_ADDRESS + ) this.setSourceToken(networkToken) state.toToken = undefined state.sourceTokenAmount = '0' @@ -278,8 +290,8 @@ export const ConvertController = { const res = await ConvertApiUtil.getTokenList() state.tokens = res.tokens - state.popularTokens = Object.entries(res.tokens) - .sort(([, aTokenInfo], [, bTokenInfo]) => { + state.popularTokens = res.tokens + .sort((aTokenInfo, bTokenInfo) => { if (aTokenInfo.symbol < bTokenInfo.symbol) { return -1 } @@ -289,25 +301,26 @@ export const ConvertController = { return 0 }) - .reduce>((limitedTokens, [tokenAddress, tokenInfo]) => { - if (ConstantsUtil.POPULAR_TOKENS.includes(tokenInfo.symbol)) { - limitedTokens[tokenAddress] = tokenInfo + .filter(token => { + if (ConstantsUtil.POPULAR_TOKENS.includes(token.symbol)) { + return true } - return limitedTokens + return false }, {}) - state.suggestedTokens = Object.entries(res.tokens).reduce>( - (limitedTokens, [tokenAddress, tokenInfo]) => { - if (ConstantsUtil.SUGGESTED_TOKENS.includes(tokenInfo.symbol)) { - limitedTokens[tokenAddress] = tokenInfo - } + state.suggestedTokens = res.tokens.filter(token => { + if (ConstantsUtil.SUGGESTED_TOKENS.includes(token.symbol)) { + return true + } - return limitedTokens - }, - {} + return false + }, {}) + const networkToken = res.tokens.find( + token => token.address === ConstantsUtil.NATIVE_TOKEN_ADDRESS ) - const networkToken = res.tokens[ConstantsUtil.NATIVE_TOKEN_ADDRESS] - this.setSourceToken(networkToken) + if (networkToken) { + this.setSourceToken(networkToken) + } return state.tokens }, @@ -315,11 +328,11 @@ export const ConvertController = { async getAddressPrice(address: string) { const existPrice = state.tokensPriceMap[address] if (existPrice) { - return parseFloat(existPrice) + return existPrice } const prices = await ConvertApiUtil.getTokenPriceWithAddresses([address]) const price = prices[address] || '0' - state.tokensPriceMap[address] = price + state.tokensPriceMap[address] = parseFloat(price) return parseFloat(price) }, @@ -329,32 +342,29 @@ export const ConvertController = { ConstantsUtil.NATIVE_TOKEN_ADDRESS ]) const price = prices[ConstantsUtil.NATIVE_TOKEN_ADDRESS] || '0' - state.tokensPriceMap[ConstantsUtil.NATIVE_TOKEN_ADDRESS] = price + state.tokensPriceMap[ConstantsUtil.NATIVE_TOKEN_ADDRESS] = parseFloat(price) state.networkPrice = price }, async getMyTokensWithBalance() { - const res = await ConvertApiUtil.getMyTokensWithBalance() + const balances = await ConvertApiUtil.getMyTokensWithBalance() - if (!res) { + if (!balances) { return } await this.getInitialGasPrice() - const networkToken = res[ConstantsUtil.NATIVE_TOKEN_ADDRESS] - - state.tokensPriceMap = Object.entries(res).reduce>( - (prices, [tokenAddress, tokenInfo]) => { - prices[tokenAddress] = tokenInfo.price - - return prices - }, - {} + const networkToken = balances.find( + token => token.address === ConstantsUtil.NATIVE_TOKEN_ADDRESS ) - state.myTokensWithBalance = res + + balances.map(token => { + state.tokensPriceMap[token.address] = token.price + }) + state.myTokensWithBalance = balances as ConvertTokenWithBalance[] state.networkBalanceInUSD = networkToken - ? NumberUtil.multiply(networkToken.balance, networkToken.price).toString() + ? NumberUtil.multiply(networkToken.quantity.numeric, networkToken.price).toString() : '0' }, diff --git a/packages/core/src/utils/ConvertApiUtil.ts b/packages/core/src/utils/ConvertApiUtil.ts index 66a9efe6a7..613563e689 100644 --- a/packages/core/src/utils/ConvertApiUtil.ts +++ b/packages/core/src/utils/ConvertApiUtil.ts @@ -4,6 +4,8 @@ import { FetchUtil } from '../utils/FetchUtil.js' import { AccountController } from '../controllers/AccountController.js' import { ConnectionController } from '../controllers/ConnectionController.js' import { ConstantsUtil } from '../utils/ConstantsUtil.js' +import { BlockchainApiController } from '../controllers/BlockchainApiController.js' +import { OptionsController } from '../controllers/OptionsController.js' const ONEINCH_API_BASE_URL = 'https://1inch-swap-proxy.walletconnect-v1-bridge.workers.dev' @@ -172,9 +174,12 @@ export const ConvertApiUtil = { }, async getTokenList() { - const { api, paths } = this.get1InchAPI() + const response = await BlockchainApiController.fetchConvertTokens({ + chainId: NetworkController.state.caipNetwork?.id, + projectId: OptionsController.state.projectId + }) - return await api.get({ path: paths.tokens }) + return response }, async searchTokens(searchTerm: string) { @@ -187,49 +192,50 @@ export const ConvertApiUtil = { }, async getMyTokensWithBalance() { - const { balances, tokenAddresses } = await this.getBalances() - - if (!tokenAddresses?.length) { - return undefined - } - - const addresses = [...tokenAddresses, ConstantsUtil.NATIVE_TOKEN_ADDRESS] - - const [tokenInfos, tokensPrices] = await Promise.all([ - this.getTokenInfoWithAddresses(addresses), - this.getTokenPriceWithAddresses(addresses) - ]) - - const mergedTokensWithBalances = this.mergeTokensWithBalanceAndPrice( - tokenInfos, - balances, - tokensPrices + const response = await BlockchainApiController.getBalance( + // @ts-ignore + AccountController.state.address, + NetworkController.state.caipNetwork?.id ) - - return mergedTokensWithBalances - }, - - async getBalances() { - const { api, paths } = this.get1InchAPI() - - const balances = await api.get>({ - path: paths.balance + const balances = response.balances + // name - Token name. + // symbol - Token symbol. + // address - Contract address of the token in CAIP-10 format. + // decimals - Number of decimals for amount supported by a given token. + // logoUri - URL of the token icon. + // eip2612 - (Optional for ERC-20 tokens) value is true if the token supports eip-2612 + + const tokens = balances.map(balance => { + return { + address: balance.address, + symbol: balance.symbol, + name: balance.name, + decimals: parseInt(balance.quantity.decimals), + logoUri: balance.iconUrl, + eip2612: false, + // balance fields + quantity: { + numeric: balance.quantity.numeric, + decimals: balance.quantity.decimals + }, + price: balance.price, + value: balance.value + } }) - const nonEmptyBalances = Object.entries(balances).reduce>( - (filteredBalances, [tokenAddress, balance]) => { - if (balance !== '0') { - filteredBalances[tokenAddress] = balance - } + // const nonEmptyBalances = balances.filter(balance => { + // if (balance.quantity.numeric !== '0') { + // return true + // } - return filteredBalances - }, - {} - ) + // return false + // }) - return { balances: nonEmptyBalances, tokenAddresses: Object.keys(nonEmptyBalances) } + return tokens }, + async getBalances() {}, + async getTokenInfoWithAddresses(addresses: string[]) { const { api, paths } = this.get1InchAPI() diff --git a/packages/core/src/utils/TypeUtil.ts b/packages/core/src/utils/TypeUtil.ts index 796eebae68..81f45da739 100644 --- a/packages/core/src/utils/TypeUtil.ts +++ b/packages/core/src/utils/TypeUtil.ts @@ -161,6 +161,24 @@ export interface BlockchainApiTransactionsResponse { next: string | null } +export type ConvertToken = { + name: string + symbol: string + address: string + decimals: number + logoUri: string + eip2612?: boolean +} + +export interface BlockchainApiConvertTokensRequest { + projectId: string + chainId?: string +} + +export interface BlockchainApiConvertTokensResponse { + tokens: ConvertToken[] +} + export interface BlockchainApiBalanceResponse { balances: Balance[] } diff --git a/packages/scaffold/src/partials/w3m-convert-input/index.ts b/packages/scaffold/src/partials/w3m-convert-input/index.ts index 74032ab3a0..a0b5b43fdf 100644 --- a/packages/scaffold/src/partials/w3m-convert-input/index.ts +++ b/packages/scaffold/src/partials/w3m-convert-input/index.ts @@ -1,6 +1,6 @@ import { html, LitElement } from 'lit' import { property } from 'lit/decorators.js' -import { EventsController, RouterController } from '@web3modal/core' +import { EventsController, RouterController, type ConvertToken } from '@web3modal/core' import { NumberUtil } from '@web3modal/common' import { UiHelperUtil, @@ -14,18 +14,6 @@ const MINIMUM_USD_VALUE_TO_CONVERT = 0.00005 type Target = 'sourceToken' | 'toToken' -interface TokenInfo { - address: `0x${string}` - symbol: string - name: string - decimals: number - logoURI: string - domainVersion?: string - eip2612?: boolean - isFoT?: boolean - tags?: string[] -} - @customElement('w3m-convert-input') export class W3mConvertInput extends LitElement { public static override styles = [styles] @@ -45,7 +33,7 @@ export class W3mConvertInput extends LitElement { @property() public target: Target = 'sourceToken' - @property() public token?: TokenInfo + @property() public token?: ConvertToken @property() public onSetAmount: ((target: Target, value: string) => void) | null = null @@ -145,8 +133,8 @@ export class W3mConvertInput extends LitElement { ` } - const tokenElement = this.token.logoURI - ? html`` + const tokenElement = this.token.logoUri + ? html`` : html` @@ -136,7 +136,7 @@ export class W3mConvertPreviewView extends LitElement { diff --git a/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts b/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts index ec4faa8922..9b4b978d56 100644 --- a/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts @@ -1,12 +1,10 @@ import { customElement, interpolate } from '@web3modal/ui' import { LitElement, html } from 'lit' import styles from './styles.js' -import { RouterController, ConvertController } from '@web3modal/core' -import type { - TokenInfo, - TokenInfoWithBalance -} from '@web3modal/core/src/controllers/ConvertController.js' +import { RouterController, ConvertController, type ConvertToken } from '@web3modal/core' import { state } from 'lit/decorators.js' +import type { Balance } from '@web3modal/common' +import type { ConvertTokenWithBalance } from '@web3modal/core/src/controllers/ConvertController.js' @customElement('w3m-convert-select-token-view') export class W3mConvertSelectTokenView extends LitElement { @@ -82,7 +80,7 @@ export class W3mConvertSelectTokenView extends LitElement { }, 5000) } - private onSelectToken(token: TokenInfo) { + private onSelectToken(token: ConvertToken) { if (this.targetToken === 'sourceToken') { ConvertController.setSourceToken(token) } else { @@ -111,13 +109,16 @@ export class W3mConvertSelectTokenView extends LitElement { ? Object.values(ConvertController.state.myTokensWithBalance) : [] const tokens = ConvertController.state.popularTokens - ? Object.values(ConvertController.state.popularTokens) + ? ConvertController.state.popularTokens : [] - const filteredYourTokens: TokenInfoWithBalance[] = this.filterTokensWithText< - TokenInfoWithBalance[] + const filteredYourTokens: ConvertTokenWithBalance[] = this.filterTokensWithText< + ConvertTokenWithBalance[] >(yourTokens, this.searchValue) - const filteredTokens = this.filterTokensWithText(tokens, this.searchValue) + const filteredTokens = this.filterTokensWithText( + tokens, + this.searchValue + ) return html` @@ -127,22 +128,22 @@ export class W3mConvertSelectTokenView extends LitElement { Your tokens - ${filteredYourTokens.map(tokenInfo => { + ${filteredYourTokens.map(token => { const selected = - tokenInfo.symbol === this.sourceToken?.symbol || - tokenInfo.symbol === this.toToken?.symbol + token.symbol === this.sourceToken?.symbol || + token.symbol === this.toToken?.symbol return html` { if (!selected) { - this.onSelectToken(tokenInfo) + this.onSelectToken(token) } }} > @@ -158,12 +159,12 @@ export class W3mConvertSelectTokenView extends LitElement { ${filteredTokens?.length > 0 ? filteredTokens.map( - tokenInfo => html` + token => html` this.onSelectToken(tokenInfo)} + name=${token.name} + symbol=${token.symbol} + imageSrc=${token.logoUri} + @click=${() => this.onSelectToken(token)} > ` @@ -176,7 +177,7 @@ export class W3mConvertSelectTokenView extends LitElement { private templateSuggestedTokens() { const tokens = ConvertController.state.suggestedTokens - ? Object.values(ConvertController.state.suggestedTokens).slice(0, 8) + ? ConvertController.state.suggestedTokens.slice(0, 8) : null if (!tokens) { @@ -186,11 +187,11 @@ export class W3mConvertSelectTokenView extends LitElement { return html` ${tokens.map( - tokenInfo => html` + token => html` this.onSelectToken(tokenInfo)} + text=${token.symbol} + imageSrc=${token.logoUri} + @click=${() => this.onSelectToken(token)} > ` @@ -247,7 +248,7 @@ export class W3mConvertSelectTokenView extends LitElement { ) } - private filterTokensWithText(tokens: TokenInfo[], text: string) { + private filterTokensWithText(tokens: Balance[] | ConvertToken[], text: string) { return tokens.filter(token => `${token.symbol} ${token.name} ${token.address}`.toLowerCase().includes(text.toLowerCase()) ) as T diff --git a/packages/scaffold/src/views/w3m-convert-view/index.ts b/packages/scaffold/src/views/w3m-convert-view/index.ts index a68a8ad4b9..3b341f97cd 100644 --- a/packages/scaffold/src/views/w3m-convert-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-view/index.ts @@ -8,10 +8,10 @@ import { CoreHelperUtil, NetworkController, ModalController, - ConstantsUtil + ConstantsUtil, + type ConvertToken } from '@web3modal/core' import { NumberUtil } from '@web3modal/common' -import type { TokenInfo } from '@web3modal/core/src/utils/ConvertApiUtil.js' type Target = 'sourceToken' | 'toToken' @@ -195,8 +195,10 @@ export class W3mConvertView extends LitElement { ` } - private templateTokenInput(target: Target, token?: TokenInfo) { - const myToken = ConvertController.state.myTokensWithBalance?.[token?.address ?? ''] + private templateTokenInput(target: Target, token?: ConvertToken) { + const myToken = ConvertController.state.myTokensWithBalance?.find( + ct => ct?.address === token?.address + ) const amount = target === 'toToken' ? this.toTokenAmount : this.sourceTokenAmount const price = target === 'toToken' ? this.toTokenPriceInUSD : this.sourceTokenPriceInUSD let value = parseFloat(amount) * price @@ -211,7 +213,7 @@ export class W3mConvertView extends LitElement { .onSetAmount=${this.handleChangeAmount.bind(this)} target=${target} .token=${token} - .balance=${myToken?.balance} + .balance=${myToken?.quantity?.numeric} .price=${this.sourceTokenPriceInUSD} .marketValue=${isNaN(value) ? '' : formatNumberToLocalString(value)} .onSetMaxValue=${this.onSetMaxValue.bind(this)} From 03a07fd9021bc46d90f6b9f4d20020daf56b954c Mon Sep 17 00:00:00 2001 From: enesozturk Date: Tue, 16 Apr 2024 20:11:54 +0300 Subject: [PATCH 82/96] refactor: switch to blockchain api for convert endpoints --- .../controllers/BlockchainApiController.ts | 67 ++++++++ .../core/src/controllers/ConvertController.ts | 157 ++++++++++++------ packages/core/src/utils/ConstantsUtil.ts | 2 +- packages/core/src/utils/ConvertApiUtil.ts | 131 +++------------ packages/core/src/utils/TypeUtil.ts | 64 ++++++- .../src/partials/w3m-convert-details/index.ts | 2 +- .../views/w3m-convert-preview-view/index.ts | 5 +- .../src/views/w3m-convert-view/index.ts | 5 +- 8 files changed, 261 insertions(+), 172 deletions(-) diff --git a/packages/core/src/controllers/BlockchainApiController.ts b/packages/core/src/controllers/BlockchainApiController.ts index 726b8b9b5b..654a7a9f41 100644 --- a/packages/core/src/controllers/BlockchainApiController.ts +++ b/packages/core/src/controllers/BlockchainApiController.ts @@ -1,3 +1,4 @@ +import { ConstantsUtil } from '../utils/ConstantsUtil.js' import { CoreHelperUtil } from '../utils/CoreHelperUtil.js' import { FetchUtil } from '../utils/FetchUtil.js' import type { @@ -5,6 +6,12 @@ import type { BlockchainApiTransactionsResponse, BlockchainApiConvertTokensRequest, BlockchainApiConvertTokensResponse, + BlockchainApiGenerateConvertCalldataRequest, + BlockchainApiGenerateConvertCalldataResponse, + BlockchainApiGenerateApproveCalldataRequest, + BlockchainApiGenerateApproveCalldataResponse, + BlockchainApiTokenPriceRequest, + BlockchainApiTokenPriceResponse, BlockchainApiIdentityRequest, BlockchainApiIdentityResponse, GenerateOnRampUrlArgs, @@ -131,9 +138,69 @@ export const BlockchainApiController = { }) }, + fetchTokenPrice({ projectId, addresses }: BlockchainApiTokenPriceRequest) { + return api.post({ + path: `/v1/fungible/price`, + body: { + projectId, + currency: 'usd', + addresses + }, + headers: { + 'content-type': 'text/json' + } + }) + }, + + generateConvertCalldata({ + amount, + from, + projectId, + to, + userAddress + }: BlockchainApiGenerateConvertCalldataRequest) { + return api.post({ + path: '/v1/convert/build-transaction', + headers: { + 'Content-Type': 'application/json' + }, + body: { + amount, + eip155: { + slippage: ConstantsUtil.CONVERT_SLIPPAGE_TOLERANCE + }, + from, + projectId, + to, + userAddress + } + }) + }, + + generateApproveCalldata({ + amount, + from, + projectId, + to, + userAddress + }: BlockchainApiGenerateApproveCalldataRequest) { + return api.get({ + path: `/v1/convert/build-approve?projectId=${projectId}&userAddress=${userAddress}&from=${from}&amount=${amount}&to=${to}`, + headers: { + 'Content-Type': 'application/json' + } + }) + }, + async getBalance(address: string, chainId?: string) { + const { sdkType, sdkVersion } = OptionsController.state + return api.get({ path: `/v1/account/${address}/balance`, + headers: { + 'x-sdk-type': sdkType, + 'x-sdk-version': sdkVersion + }, params: { currency: 'usd', projectId: OptionsController.state.projectId, diff --git a/packages/core/src/controllers/ConvertController.ts b/packages/core/src/controllers/ConvertController.ts index ca1ac06770..014a49c63e 100644 --- a/packages/core/src/controllers/ConvertController.ts +++ b/packages/core/src/controllers/ConvertController.ts @@ -8,13 +8,17 @@ import { SnackController } from './SnackController.js' import { RouterController } from './RouterController.js' import { NumberUtil } from '@web3modal/common' import type { ConvertToken } from '../utils/TypeUtil.js' +import { NetworkController } from './NetworkController.js' +import { CoreHelperUtil } from '../utils/CoreHelperUtil.js' +import { BlockchainApiController } from './BlockchainApiController.js' +import { OptionsController } from './OptionsController.js' export const INITIAL_GAS_LIMIT = 150000 // -- Types --------------------------------------------- // type TransactionParams = { - data: `0x${string}` - to: `0x${string}` + data: string + to: string gas: bigint gasPrice: bigint value: bigint @@ -64,7 +68,7 @@ export interface ConvertControllerState { inputError: string | undefined // Request values - slippage: string + slippage: number // Tokens tokens?: ConvertTokenWithBalance[] @@ -160,13 +164,16 @@ export const ConvertController = { getParams() { const { address } = AccountController.state + const networkAddress = `${NetworkController.state.caipNetwork?.id}:${ConstantsUtil.NATIVE_TOKEN_ADDRESS}` if (!address) { throw new Error('No address found to swap the tokens from.') } return { - fromAddress: address as `0x${string}`, + networkAddress, + fromAddress: address, + fromCaipAddress: AccountController.state.caipAddress, sourceTokenAddress: state.sourceToken?.address, toTokenAddress: state.toToken?.address, toTokenAmount: state.toTokenAmount, @@ -225,7 +232,7 @@ export const ConvertController = { }, async setTokenValues(address: string, target: 'sourceToken' | 'toToken') { - let price = state.tokensPriceMap[address] || 0 + let price = state.tokensPriceMap[`${NetworkController.state.caipNetwork?.id}:${address}`] || 0 if (!price) { price = await this.getAddressPrice(address) @@ -257,9 +264,9 @@ export const ConvertController = { }, resetValues() { - const networkToken = state.tokens?.find( - token => token.address === ConstantsUtil.NATIVE_TOKEN_ADDRESS - ) + const { networkAddress } = this.getParams() + + const networkToken = state.tokens?.find(token => token.address === networkAddress) this.setSourceToken(networkToken) state.toToken = undefined state.sourceTokenAmount = '0' @@ -281,13 +288,24 @@ export const ConvertController = { }, async fetchTokens() { + const { networkAddress } = this.getParams() + await this.getTokenList() await this.getNetworkTokenPrice() await this.getMyTokensWithBalance() + + const networkToken = state.tokens?.find(token => token.address === networkAddress) + + if (networkToken) { + this.setSourceToken(networkToken) + } }, async getTokenList() { - const res = await ConvertApiUtil.getTokenList() + const res = await BlockchainApiController.fetchConvertTokens({ + chainId: NetworkController.state.caipNetwork?.id, + projectId: OptionsController.state.projectId + }) state.tokens = res.tokens state.popularTokens = res.tokens @@ -315,14 +333,6 @@ export const ConvertController = { return false }, {}) - const networkToken = res.tokens.find( - token => token.address === ConstantsUtil.NATIVE_TOKEN_ADDRESS - ) - if (networkToken) { - this.setSourceToken(networkToken) - } - - return state.tokens }, async getAddressPrice(address: string) { @@ -330,6 +340,13 @@ export const ConvertController = { if (existPrice) { return existPrice } + // TODO: replace with blockchain api + // const response = await BlockchainApiController.fetchTokenPrice({ + // projectId: OptionsController.state.projectId, + // addresses: [address] + // }) + // const prices = response.fungibles + // const price = prices.find(p => p.address === address)?.price || '0' const prices = await ConvertApiUtil.getTokenPriceWithAddresses([address]) const price = prices[address] || '0' state.tokensPriceMap[address] = parseFloat(price) @@ -338,15 +355,26 @@ export const ConvertController = { }, async getNetworkTokenPrice() { + const { networkAddress } = this.getParams() + + // TODO: replace with blockchain api + // const response = await BlockchainApiController.fetchTokenPrice({ + // projectId: OptionsController.state.projectId, + // addresses: [networkAddress] + // }) + // const prices = response.fungibles + // const price = prices.find(p => p.address === networkAddress)?.price || '0' const prices = await ConvertApiUtil.getTokenPriceWithAddresses([ ConstantsUtil.NATIVE_TOKEN_ADDRESS ]) - const price = prices[ConstantsUtil.NATIVE_TOKEN_ADDRESS] || '0' - state.tokensPriceMap[ConstantsUtil.NATIVE_TOKEN_ADDRESS] = parseFloat(price) + const price = prices[networkAddress] || '0' + state.tokensPriceMap[networkAddress] = parseFloat(price) + console.log('>>> [state.networkPrice]: ', price, prices) state.networkPrice = price }, async getMyTokensWithBalance() { + const { networkAddress } = this.getParams() const balances = await ConvertApiUtil.getMyTokensWithBalance() if (!balances) { @@ -355,11 +383,9 @@ export const ConvertController = { await this.getInitialGasPrice() - const networkToken = balances.find( - token => token.address === ConstantsUtil.NATIVE_TOKEN_ADDRESS - ) + const networkToken = balances.find(token => token.address === networkAddress) - balances.map(token => { + balances.forEach(token => { state.tokensPriceMap[token.address] = token.price }) state.myTokensWithBalance = balances as ConvertTokenWithBalance[] @@ -462,9 +488,10 @@ export const ConvertController = { return null } + // TODO: replace with blockchain api const hasAllowance = await ConvertApiUtil.checkConvertAllowance({ fromAddress, - sourceTokenAddress, + sourceTokenAddress: CoreHelperUtil.getPlainAddress(sourceTokenAddress) as string, sourceTokenAmount, sourceTokenDecimals }) @@ -473,12 +500,10 @@ export const ConvertController = { if (hasAllowance) { state.approvalTransaction = undefined - transaction = await this.createConvert() - state.convertTransaction = transaction || undefined + state.convertTransaction = await this.createConvert() } else { state.convertTransaction = undefined - transaction = await this.createTokenAllowance() - state.approvalTransaction = transaction + state.approvalTransaction = await this.createTokenAllowance() } return transaction @@ -500,19 +525,29 @@ export const ConvertController = { }, async createTokenAllowance() { - const { fromAddress, sourceTokenAddress } = this.getParams() + const { fromCaipAddress, fromAddress, sourceTokenAddress, sourceTokenAmount, toTokenAddress } = + this.getParams() + + if (!fromCaipAddress || !toTokenAddress) { + return undefined + } if (!sourceTokenAddress) { throw new Error('>>> createTokenAllowance - No source token address found.') } - const transaction = await ConvertApiUtil.getConvertApprovalData({ - sourceTokenAddress + const response = await BlockchainApiController.generateApproveCalldata({ + projectId: OptionsController.state.projectId, + from: sourceTokenAddress, + amount: parseInt(sourceTokenAmount), // Todo: this is not used right now but we should update find the best way/format to set it + to: toTokenAddress, + userAddress: fromCaipAddress }) + const transaction = response.tx const gasLimit = await ConnectionController.getEstimatedGas({ - address: fromAddress, - to: transaction.to, + address: fromAddress as `0x${string}`, + to: transaction.to as `0x${string}`, data: transaction.data }) @@ -521,7 +556,7 @@ export const ConvertController = { return { ...transaction, gas: gasLimit, - gasPrice: BigInt(transaction.gasPrice), + gasPrice: BigInt(transaction.eip155?.gasPrice), value: BigInt(transaction.value), toAmount } @@ -538,9 +573,9 @@ export const ConvertController = { try { await ConnectionController.sendTransaction({ - address: fromAddress, - to: data.to, - data: data.data, + address: fromAddress as `0x${string}`, + to: data.to as `0x${string}`, + data: data.data as `0x${string}`, value: BigInt(data.value), gasPrice: BigInt(data.gasPrice) }) @@ -557,43 +592,55 @@ export const ConvertController = { async createConvert() { const { - fromAddress, + fromCaipAddress, sourceTokenAddress, sourceTokenDecimals, sourceTokenAmount, toTokenAddress } = this.getParams() - if (!sourceTokenAmount || !sourceTokenAddress || !toTokenAddress || !sourceTokenDecimals) { - return null + if ( + !fromCaipAddress || + !sourceTokenAmount || + !sourceTokenAddress || + !toTokenAddress || + !sourceTokenDecimals + ) { + return undefined } try { - const response = await ConvertApiUtil.getConvertData({ - fromAddress, - sourceTokenAddress, - sourceTokenAmount, - toTokenAddress, - decimals: sourceTokenDecimals + const amount = parseInt( + ConnectionController.parseUnits(sourceTokenAmount, sourceTokenDecimals).toString() + ) + + const response = await BlockchainApiController.generateConvertCalldata({ + projectId: OptionsController.state.projectId, + userAddress: fromCaipAddress, + from: sourceTokenAddress, + to: toTokenAddress, + amount }) + const toAmount = this.getToAmount() + const transaction = { data: response.tx.data, to: response.tx.to, - gas: BigInt(response.tx.gas), - gasPrice: BigInt(response.tx.gasPrice), - value: BigInt(response.tx.value), - toAmount: response.toAmount + gas: BigInt(parseInt(response.tx.eip155.gas)), + gasPrice: BigInt(response.tx.eip155.gasPrice), + value: BigInt(amount), + toAmount: toAmount } state.gasPriceInUSD = this.calculateGasPriceInUSD( - BigInt(response.tx.gas), + BigInt(parseInt(response.tx.eip155.gas)), transaction.gasPrice ) return transaction } catch (error) { - return null + return undefined } }, @@ -616,9 +663,9 @@ export const ConvertController = { try { const transactionHash = await ConnectionController.sendTransaction({ - address: fromAddress, - to: data.to, - data: data.data, + address: fromAddress as `0x${string}`, + to: data.to as `0x${string}`, + data: data.data as `0x${string}`, gas: data.gas, gasPrice: BigInt(data.gasPrice), value: data.value diff --git a/packages/core/src/utils/ConstantsUtil.ts b/packages/core/src/utils/ConstantsUtil.ts index 8aa5412370..63d9aaf66a 100644 --- a/packages/core/src/utils/ConstantsUtil.ts +++ b/packages/core/src/utils/ConstantsUtil.ts @@ -155,7 +155,7 @@ export const ConstantsUtil = { NATIVE_TOKEN_ADDRESS: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee', - CONVERT_SLIPPAGE_TOLERANCE: '0.5' + CONVERT_SLIPPAGE_TOLERANCE: 1 } export type CoinbasePaySDKChainNameValues = diff --git a/packages/core/src/utils/ConvertApiUtil.ts b/packages/core/src/utils/ConvertApiUtil.ts index 613563e689..34de4fa21a 100644 --- a/packages/core/src/utils/ConvertApiUtil.ts +++ b/packages/core/src/utils/ConvertApiUtil.ts @@ -5,7 +5,6 @@ import { AccountController } from '../controllers/AccountController.js' import { ConnectionController } from '../controllers/ConnectionController.js' import { ConstantsUtil } from '../utils/ConstantsUtil.js' import { BlockchainApiController } from '../controllers/BlockchainApiController.js' -import { OptionsController } from '../controllers/OptionsController.js' const ONEINCH_API_BASE_URL = 'https://1inch-swap-proxy.walletconnect-v1-bridge.workers.dev' @@ -173,24 +172,6 @@ export const ConvertApiUtil = { return false }, - async getTokenList() { - const response = await BlockchainApiController.fetchConvertTokens({ - chainId: NetworkController.state.caipNetwork?.id, - projectId: OptionsController.state.projectId - }) - - return response - }, - - async searchTokens(searchTerm: string) { - const { api, paths } = this.get1InchAPI() - - return await api.get({ - path: paths.search, - params: { query: searchTerm } - }) - }, - async getMyTokensWithBalance() { const response = await BlockchainApiController.getBalance( // @ts-ignore @@ -198,63 +179,46 @@ export const ConvertApiUtil = { NetworkController.state.caipNetwork?.id ) const balances = response.balances - // name - Token name. - // symbol - Token symbol. - // address - Contract address of the token in CAIP-10 format. - // decimals - Number of decimals for amount supported by a given token. - // logoUri - URL of the token icon. - // eip2612 - (Optional for ERC-20 tokens) value is true if the token supports eip-2612 - - const tokens = balances.map(balance => { + + const tokens = balances.map(token => { return { - address: balance.address, - symbol: balance.symbol, - name: balance.name, - decimals: parseInt(balance.quantity.decimals), - logoUri: balance.iconUrl, + symbol: token.symbol, + name: token.name, + address: !!token.address + ? token.address + : `${NetworkController.state.caipNetwork?.id}:${ConstantsUtil.NATIVE_TOKEN_ADDRESS}`, + decimals: parseInt(token.quantity.decimals), + logoUri: token.iconUrl, eip2612: false, - // balance fields quantity: { - numeric: balance.quantity.numeric, - decimals: balance.quantity.decimals + numeric: token.quantity.numeric, + decimals: token.quantity.decimals }, - price: balance.price, - value: balance.value + price: token.price, + value: token.value } }) - // const nonEmptyBalances = balances.filter(balance => { - // if (balance.quantity.numeric !== '0') { - // return true - // } - - // return false - // }) - return tokens }, - async getBalances() {}, - - async getTokenInfoWithAddresses(addresses: string[]) { - const { api, paths } = this.get1InchAPI() - - return api.get>({ - path: paths.tokensCustom, - params: { addresses: addresses.join(',') } - }) - }, - async getTokenPriceWithAddresses(addresses: string[]) { const { api, paths } = this.get1InchAPI() - return await api.post>({ + const values = await api.post>({ path: paths.tokenPrices, body: { tokens: addresses, currency: 'USD' }, headers: { 'content-type': 'application/json' } }) + + // return same values but update the keys with `eip155:137:` prefix to mimic blockchain api response + return Object.entries(values).reduce>((_values, [key, value]) => { + _values[`${NetworkController.state.caipNetwork?.id}:${key}`] = value + + return _values + }, {}) }, mergeTokensWithBalanceAndPrice( @@ -279,58 +243,5 @@ export const ConvertApiUtil = { ) return mergedTokens - }, - - async getConvertData({ - sourceTokenAddress, - toTokenAddress, - sourceTokenAmount, - fromAddress, - decimals = 9 - }: GetConvertDataParams): Promise { - const { api, paths } = this.get1InchAPI() - - return await api.get({ - path: paths.swap, - params: { - src: sourceTokenAddress, - dst: toTokenAddress, - slippage: ConstantsUtil.CONVERT_SLIPPAGE_TOLERANCE, - from: fromAddress, - amount: ConnectionController.parseUnits(sourceTokenAmount, decimals).toString() - } - }) - }, - - async getConvertApprovalData({ sourceTokenAddress }: GetApprovalParams) { - const { api, paths } = this.get1InchAPI() - - return await api.get({ - path: paths.approveTransaction, - params: { - tokenAddress: sourceTokenAddress - } - }) - }, - - async getQuoteApprovalData({ - sourceTokenAddress, - toTokenAddress, - sourceTokenAmount, - fromAddress, - decimals = 9 - }: GetConvertDataParams): Promise { - const { api, paths } = this.get1InchAPI() - - return await api.get({ - path: paths.quote, - params: { - src: sourceTokenAddress, - dst: toTokenAddress, - slippage: ConstantsUtil.CONVERT_SLIPPAGE_TOLERANCE, - from: fromAddress, - amount: ConnectionController.parseUnits(sourceTokenAmount, decimals).toString() - } - }) } } diff --git a/packages/core/src/utils/TypeUtil.ts b/packages/core/src/utils/TypeUtil.ts index 81f45da739..8b599e4841 100644 --- a/packages/core/src/utils/TypeUtil.ts +++ b/packages/core/src/utils/TypeUtil.ts @@ -164,7 +164,7 @@ export interface BlockchainApiTransactionsResponse { export type ConvertToken = { name: string symbol: string - address: string + address: `${string}:${string}:${string}` decimals: number logoUri: string eip2612?: boolean @@ -179,6 +179,68 @@ export interface BlockchainApiConvertTokensResponse { tokens: ConvertToken[] } +export interface BlockchainApiTokenPriceRequest { + projectId: string + currency?: 'usd' | 'eur' | 'gbp' | 'aud' | 'cad' | 'inr' | 'jpy' | 'btc' | 'eth' + addresses: string[] +} + +export interface BlockchainApiTokenPriceResponse { + fungibles: { + address: string + name: string + symbol: string + iconUrl: string + price: string + }[] +} + +export interface BlockchainApiGenerateConvertCalldataRequest { + projectId: string + userAddress: string + from: string + to: string + amount: number + eip155?: { + slippage: string + permit?: string + } +} + +export interface BlockchainApiGenerateConvertCalldataResponse { + tx: { + from: string + to: `0x${string}` + data: `0x${string}` + amount: string + eip155: { + gas: string + gasPrice: string + } + } +} + +export interface BlockchainApiGenerateApproveCalldataRequest { + projectId: string + userAddress: string + from: string + to: string + amount: number +} + +export interface BlockchainApiGenerateApproveCalldataResponse { + tx: { + from: string + to: `0x${string}` + data: `0x${string}` + value: string + eip155: { + gas: number + gasPrice: string + } + } +} + export interface BlockchainApiBalanceResponse { balances: Balance[] } diff --git a/packages/scaffold/src/partials/w3m-convert-details/index.ts b/packages/scaffold/src/partials/w3m-convert-details/index.ts index 9f8f4de0cd..b78bf3edd9 100644 --- a/packages/scaffold/src/partials/w3m-convert-details/index.ts +++ b/packages/scaffold/src/partials/w3m-convert-details/index.ts @@ -22,7 +22,7 @@ export class WuiConvertDetails extends LitElement { @property() public priceImpact?: number - @property() public slippageRate = 0.5 + @property() public slippageRate = 1 @property() public maxSlippage?: number diff --git a/packages/scaffold/src/views/w3m-convert-preview-view/index.ts b/packages/scaffold/src/views/w3m-convert-preview-view/index.ts index 1711748d2e..c1f6f2a1e6 100644 --- a/packages/scaffold/src/views/w3m-convert-preview-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-preview-view/index.ts @@ -5,7 +5,8 @@ import { AccountController, NetworkController, RouterController, - ConvertController + ConvertController, + ConstantsUtil } from '@web3modal/core' import { state } from 'lit/decorators.js' @@ -194,7 +195,7 @@ export class W3mConvertPreviewView extends LitElement { toTokenConvertedAmount=${toTokenConvertedAmount} gasPriceInUSD=${formatNumberToLocalString(this.gasPriceInUSD, 3)} .priceImpact=${this.priceImpact} - slippageRate=${0.5} + slippageRate=${ConstantsUtil.CONVERT_SLIPPAGE_TOLERANCE} .maxSlippage=${this.maxSlippage} > ` diff --git a/packages/scaffold/src/views/w3m-convert-view/index.ts b/packages/scaffold/src/views/w3m-convert-view/index.ts index 3b341f97cd..7fe8851b86 100644 --- a/packages/scaffold/src/views/w3m-convert-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-view/index.ts @@ -106,7 +106,8 @@ export class W3mConvertView extends LitElement { ] ) - this.watchTokensAndValues() + // TODO: handle watching tokens + // this.watchTokensAndValues() } public override firstUpdated() { @@ -273,7 +274,7 @@ export class W3mConvertView extends LitElement { toTokenConvertedAmount=${toTokenConvertedAmount} gasPriceInUSD=${this.gasPriceInUSD} .priceImpact=${this.priceImpact} - slippageRate=${0.5} + slippageRate=${ConstantsUtil.CONVERT_SLIPPAGE_TOLERANCE} .maxSlippage=${this.maxSlippage} > ` From 0645a37584904ed4e6e93a9fd2743f60f59136d0 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Wed, 17 Apr 2024 10:22:11 +0300 Subject: [PATCH 83/96] refactor: update type definitions --- .../core/src/controllers/ConvertController.ts | 48 ++++------ packages/core/src/utils/ConvertApiUtil.ts | 31 +++++- packages/core/src/utils/TypeUtil.ts | 9 ++ .../controllers/ConvertController.test.ts | 14 ++- .../tests/controllers/SendController.test.ts | 1 + .../core/tests/mocks/ConvertController.ts | 95 +++++++++++-------- .../w3m-convert-select-token-view/index.ts | 8 +- .../src/views/w3m-convert-view/index.ts | 5 +- 8 files changed, 122 insertions(+), 89 deletions(-) diff --git a/packages/core/src/controllers/ConvertController.ts b/packages/core/src/controllers/ConvertController.ts index 014a49c63e..de62d0054f 100644 --- a/packages/core/src/controllers/ConvertController.ts +++ b/packages/core/src/controllers/ConvertController.ts @@ -7,7 +7,7 @@ import { ConvertApiUtil } from '../utils/ConvertApiUtil.js' import { SnackController } from './SnackController.js' import { RouterController } from './RouterController.js' import { NumberUtil } from '@web3modal/common' -import type { ConvertToken } from '../utils/TypeUtil.js' +import type { ConvertTokenWithBalance } from '../utils/TypeUtil.js' import { NetworkController } from './NetworkController.js' import { CoreHelperUtil } from '../utils/CoreHelperUtil.js' import { BlockchainApiController } from './BlockchainApiController.js' @@ -25,15 +25,6 @@ type TransactionParams = { toAmount: string } -export type ConvertTokenWithBalance = ConvertToken & { - quantity?: { - numeric: string - decimals: string - } - price?: number - value?: number -} - class TransactionError extends Error { shortMessage?: string @@ -302,13 +293,10 @@ export const ConvertController = { }, async getTokenList() { - const res = await BlockchainApiController.fetchConvertTokens({ - chainId: NetworkController.state.caipNetwork?.id, - projectId: OptionsController.state.projectId - }) + const tokens = await ConvertApiUtil.getTokenList() - state.tokens = res.tokens - state.popularTokens = res.tokens + state.tokens = tokens + state.popularTokens = tokens .sort((aTokenInfo, bTokenInfo) => { if (aTokenInfo.symbol < bTokenInfo.symbol) { return -1 @@ -326,7 +314,7 @@ export const ConvertController = { return false }, {}) - state.suggestedTokens = res.tokens.filter(token => { + state.suggestedTokens = tokens.filter(token => { if (ConstantsUtil.SUGGESTED_TOKENS.includes(token.symbol)) { return true } @@ -369,7 +357,6 @@ export const ConvertController = { ]) const price = prices[networkAddress] || '0' state.tokensPriceMap[networkAddress] = parseFloat(price) - console.log('>>> [state.networkPrice]: ', price, prices) state.networkPrice = price }, @@ -386,7 +373,7 @@ export const ConvertController = { const networkToken = balances.find(token => token.address === networkAddress) balances.forEach(token => { - state.tokensPriceMap[token.address] = token.price + state.tokensPriceMap[token.address] = token.price || 0 }) state.myTokensWithBalance = balances as ConvertTokenWithBalance[] state.networkBalanceInUSD = networkToken @@ -485,7 +472,7 @@ export const ConvertController = { parseFloat(sourceTokenAmount) === 0 || !sourceTokenDecimals ) { - return null + return undefined } // TODO: replace with blockchain api @@ -496,14 +483,16 @@ export const ConvertController = { sourceTokenDecimals }) - let transaction: TransactionParams | null = null + let transaction: TransactionParams | undefined = undefined if (hasAllowance) { state.approvalTransaction = undefined - state.convertTransaction = await this.createConvert() + transaction = await this.createConvert() + state.convertTransaction = transaction } else { state.convertTransaction = undefined - state.approvalTransaction = await this.createTokenAllowance() + transaction = await this.createTokenAllowance() + state.approvalTransaction = transaction } return transaction @@ -623,20 +612,19 @@ export const ConvertController = { }) const toAmount = this.getToAmount() + const gas = BigInt(parseInt(response.tx.eip155.gas)) + const gasPrice = BigInt(response.tx.eip155.gasPrice) const transaction = { data: response.tx.data, to: response.tx.to, - gas: BigInt(parseInt(response.tx.eip155.gas)), - gasPrice: BigInt(response.tx.eip155.gasPrice), + gas, + gasPrice, value: BigInt(amount), toAmount: toAmount } - state.gasPriceInUSD = this.calculateGasPriceInUSD( - BigInt(parseInt(response.tx.eip155.gas)), - transaction.gasPrice - ) + state.gasPriceInUSD = this.calculateGasPriceInUSD(gas, gasPrice) return transaction } catch (error) { @@ -716,7 +704,7 @@ export const ConvertController = { ) }, - setTransactionDetails(transaction: TransactionParams | null) { + setTransactionDetails(transaction: TransactionParams | undefined) { const { toTokenAddress, toTokenDecimals } = this.getParams() if (!transaction || !toTokenAddress || !toTokenDecimals) { diff --git a/packages/core/src/utils/ConvertApiUtil.ts b/packages/core/src/utils/ConvertApiUtil.ts index 34de4fa21a..ad0f5e8a20 100644 --- a/packages/core/src/utils/ConvertApiUtil.ts +++ b/packages/core/src/utils/ConvertApiUtil.ts @@ -5,6 +5,8 @@ import { AccountController } from '../controllers/AccountController.js' import { ConnectionController } from '../controllers/ConnectionController.js' import { ConstantsUtil } from '../utils/ConstantsUtil.js' import { BlockchainApiController } from '../controllers/BlockchainApiController.js' +import type { ConvertTokenWithBalance } from './TypeUtil.js' +import { OptionsController } from '../controllers/OptionsController.js' const ONEINCH_API_BASE_URL = 'https://1inch-swap-proxy.walletconnect-v1-bridge.workers.dev' @@ -138,6 +140,28 @@ export const ConvertApiUtil = { } }, + async getTokenList() { + const response = await await BlockchainApiController.fetchConvertTokens({ + chainId: NetworkController.state.caipNetwork?.id, + projectId: OptionsController.state.projectId + }) + + const tokens = response.tokens.map(token => { + return { + ...token, + eip2612: false, + quantity: { + decimals: '0', + numeric: '0' + }, + price: 0, + value: 0 + } as ConvertTokenWithBalance + }) + + return tokens + }, + async getGasPrice() { const { api, paths } = this.get1InchAPI() @@ -190,13 +214,10 @@ export const ConvertApiUtil = { decimals: parseInt(token.quantity.decimals), logoUri: token.iconUrl, eip2612: false, - quantity: { - numeric: token.quantity.numeric, - decimals: token.quantity.decimals - }, + quantity: token.quantity, price: token.price, value: token.value - } + } as ConvertTokenWithBalance }) return tokens diff --git a/packages/core/src/utils/TypeUtil.ts b/packages/core/src/utils/TypeUtil.ts index 8b599e4841..d16c0e420b 100644 --- a/packages/core/src/utils/TypeUtil.ts +++ b/packages/core/src/utils/TypeUtil.ts @@ -170,6 +170,15 @@ export type ConvertToken = { eip2612?: boolean } +export type ConvertTokenWithBalance = ConvertToken & { + quantity: { + decimals: string + numeric: string + } + price: number + value: number +} + export interface BlockchainApiConvertTokensRequest { projectId: string chainId?: string diff --git a/packages/core/tests/controllers/ConvertController.test.ts b/packages/core/tests/controllers/ConvertController.test.ts index f34c515f6b..55cc2471ab 100644 --- a/packages/core/tests/controllers/ConvertController.test.ts +++ b/packages/core/tests/controllers/ConvertController.test.ts @@ -1,7 +1,6 @@ import { beforeAll, describe, expect, it } from 'vitest' -import { AccountController, ConvertController } from '../../index.js' +import { AccountController, ConvertController, type ConvertTokenWithBalance } from '../../index.js' import { prices, tokenInfo } from '../mocks/ConvertController.js' -import type { TokenInfo } from '../../src/utils/ConvertApiUtil.js' import { INITIAL_GAS_LIMIT } from '../../src/controllers/ConvertController.js' // - Mocks --------------------------------------------------------------------- @@ -9,17 +8,16 @@ const caipAddress = 'eip155:1:0x123' const gasLimit = BigInt(INITIAL_GAS_LIMIT) const gasFee = BigInt(455966887160) -const sourceTokenAddress = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee' -const toTokenAddress = '0xb33eaad8d922b1083446dc23f610c2567fb5180f' +const sourceTokenAddress = 'eip155:137:0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee' -const sourceToken = tokenInfo[sourceTokenAddress] as TokenInfo -const toToken = tokenInfo[toTokenAddress] as TokenInfo +const sourceToken = tokenInfo[0] as ConvertTokenWithBalance +const toToken = tokenInfo[1] as ConvertTokenWithBalance // - Helpers function setSourceTokenAmount(value: string) { ConvertController.setSourceTokenAmount(value) const toTokenAmount = ConvertController.getToAmount() - const toTokenValues = ConvertController.getToTokenValues(toTokenAmount, toToken.decimals) + const toTokenValues = ConvertController.getToTokenValues(toTokenAmount, toToken?.decimals) ConvertController.state.toTokenAmount = toTokenValues.toTokenAmount ConvertController.state.toTokenPriceInUSD = toTokenValues.toTokenPriceInUSD } @@ -28,7 +26,7 @@ function setSourceTokenAmount(value: string) { beforeAll(() => { AccountController.setCaipAddress(caipAddress) ConvertController.state.tokensPriceMap = prices - ConvertController.state.networkPrice = prices[sourceTokenAddress] + ConvertController.state.networkPrice = prices[sourceTokenAddress].toString() ConvertController.state.networkBalanceInUSD = '2' ConvertController.state.gasPriceInUSD = ConvertController.calculateGasPriceInUSD(gasLimit, gasFee) ConvertController.setSourceToken(sourceToken) diff --git a/packages/core/tests/controllers/SendController.test.ts b/packages/core/tests/controllers/SendController.test.ts index 0adfaecbcf..05389dccbd 100644 --- a/packages/core/tests/controllers/SendController.test.ts +++ b/packages/core/tests/controllers/SendController.test.ts @@ -4,6 +4,7 @@ import { SendController } from '../../index.js' // -- Setup -------------------------------------------------------------------- const token = { name: 'Optimism', + address: 'eip155:10:0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee', symbol: 'OP', chainId: 'eip155:10', value: 6.05441523113072, diff --git a/packages/core/tests/mocks/ConvertController.ts b/packages/core/tests/mocks/ConvertController.ts index 6622cbb5db..89a932f5f2 100644 --- a/packages/core/tests/mocks/ConvertController.ts +++ b/packages/core/tests/mocks/ConvertController.ts @@ -1,45 +1,64 @@ -export const tokenInfo = { - '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee': { - address: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee', - chainId: 137, - decimals: 18, - name: 'MATIC', +export const tokenInfo = [ + { + name: 'Matic Token', symbol: 'MATIC', - providers: ['1inch', 'Curve Token List'], - logoURI: 'https://tokens.1inch.io/0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0.png', - tags: ['native'] + chainId: 'eip155:137', + value: 15.945686877137186, + price: 0.6990173876, + decimals: 18, + quantity: { + numeric: '22.811574018044047908', + decimals: '18' + }, + logoUri: 'https://token-icons.s3.amazonaws.com/0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0.png' + }, + { + name: 'Tether USD', + symbol: 'USDT', + chainId: 'eip155:137', + address: 'eip155:137:0xc2132d05d31c914a87c6611c10748aeb04b58e8f', + value: 0.8888156632489365, + price: 1.0000570041, + decimals: 6, + quantity: { + numeric: '0.888765', + decimals: '6' + }, + logoUri: 'https://token-icons.s3.amazonaws.com/0xdac17f958d2ee523a2206206994597c13d831ec7.png' + }, + { + name: 'ShapeShift FOX', + symbol: 'FOX', + chainId: 'eip155:137', + address: 'eip155:137:0x65a05db8322701724c197af82c9cae41195b0aa8', + value: 0.818151429070586, + price: 0.0875161861, + decimals: 18, + quantity: { + numeric: '9.348572710146769370', + decimals: '18' + }, + logoUri: 'https://token-icons.s3.amazonaws.com/0xc770eefad204b5180df6a14ee197d99d808ee52d.png' }, - '0xb33eaad8d922b1083446dc23f610c2567fb5180f': { - address: '0xb33eaad8d922b1083446dc23f610c2567fb5180f', - chainId: 137, + { + name: 'SMARTMALL TOKEN', + symbol: 'SMT', + chainId: 'eip155:137', + address: 'eip155:137:0x658cda444ac43b0a7da13d638700931319b64014', + price: 0, decimals: 18, - name: 'Uniswap', - symbol: 'UNI', - providers: [ - '1inch', - 'CoinGecko', - 'Kleros Tokens', - 'Quickswap Token List', - 'Trust Wallet Assets', - 'Uniswap Labs Default' - ], - logoURI: 'https://tokens.1inch.io/0x1f9840a85d5af5bf1d1762f925bdaddc4201f984.png', - tags: ['tokens'] + quantity: { + numeric: '0.110019000000000000', + decimals: '18' + }, + logoUri: '' } -} - -export const balance = { - '0x65a05db8322701724c197af82c9cae41195b0aa8': '9348572710146769370', - '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee': '4960248983278245480', - '0xb33eaad8d922b1083446dc23f610c2567fb5180f': '564297973603949455', - '0xc2132d05d31c914a87c6611c10748aeb04b58e8f': '888765', - '0x2c89bbc92bd86f8075d1decc58c7f4e0107f286b': '219869721034228067' -} +] export const prices = { - '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee': '0.93508248', - '0x65a05db8322701724c197af82c9cae41195b0aa8': '0.10315220553291868', - '0xb33eaad8d922b1083446dc23f610c2567fb5180f': '11.772471201328177', - '0xc2132d05d31c914a87c6611c10748aeb04b58e8f': '0.9995840116762155', - '0x2c89bbc92bd86f8075d1decc58c7f4e0107f286b': '52.09209274652567' + 'eip155:137:0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee': 0.93508248, + 'eip155:137:0x65a05db8322701724c197af82c9cae41195b0aa8': 0.10315220553291868, + 'eip155:137:0xb33eaad8d922b1083446dc23f610c2567fb5180f': 11.772471201328177, + 'eip155:137:0xc2132d05d31c914a87c6611c10748aeb04b58e8f': 0.9995840116762155, + 'eip155:137:0x2c89bbc92bd86f8075d1decc58c7f4e0107f286b': 52.0920927465256 } diff --git a/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts b/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts index 9b4b978d56..d433470e19 100644 --- a/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts @@ -1,10 +1,8 @@ import { customElement, interpolate } from '@web3modal/ui' import { LitElement, html } from 'lit' import styles from './styles.js' -import { RouterController, ConvertController, type ConvertToken } from '@web3modal/core' +import { RouterController, ConvertController, type ConvertTokenWithBalance } from '@web3modal/core' import { state } from 'lit/decorators.js' -import type { Balance } from '@web3modal/common' -import type { ConvertTokenWithBalance } from '@web3modal/core/src/controllers/ConvertController.js' @customElement('w3m-convert-select-token-view') export class W3mConvertSelectTokenView extends LitElement { @@ -80,7 +78,7 @@ export class W3mConvertSelectTokenView extends LitElement { }, 5000) } - private onSelectToken(token: ConvertToken) { + private onSelectToken(token: ConvertTokenWithBalance) { if (this.targetToken === 'sourceToken') { ConvertController.setSourceToken(token) } else { @@ -248,7 +246,7 @@ export class W3mConvertSelectTokenView extends LitElement { ) } - private filterTokensWithText(tokens: Balance[] | ConvertToken[], text: string) { + private filterTokensWithText(tokens: ConvertTokenWithBalance[], text: string) { return tokens.filter(token => `${token.symbol} ${token.name} ${token.address}`.toLowerCase().includes(text.toLowerCase()) ) as T diff --git a/packages/scaffold/src/views/w3m-convert-view/index.ts b/packages/scaffold/src/views/w3m-convert-view/index.ts index 7fe8851b86..bd68d8d670 100644 --- a/packages/scaffold/src/views/w3m-convert-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-view/index.ts @@ -106,8 +106,7 @@ export class W3mConvertView extends LitElement { ] ) - // TODO: handle watching tokens - // this.watchTokensAndValues() + this.watchTokensAndValues() } public override firstUpdated() { @@ -137,7 +136,7 @@ export class W3mConvertView extends LitElement { ConvertController.getNetworkTokenPrice() ConvertController.getMyTokensWithBalance() ConvertController.refreshConvertValues() - }, 5000) + }, 20000) } private templateSwap() { From e9ab90f1d27866a183013e81271a37d0fc7d00a0 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Wed, 17 Apr 2024 12:07:01 +0300 Subject: [PATCH 84/96] refactor: approve tx params and type updates --- .../core/src/controllers/ConvertController.ts | 20 +++++++++++-------- packages/core/src/utils/TypeUtil.ts | 8 ++++---- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/packages/core/src/controllers/ConvertController.ts b/packages/core/src/controllers/ConvertController.ts index de62d0054f..810a034054 100644 --- a/packages/core/src/controllers/ConvertController.ts +++ b/packages/core/src/controllers/ConvertController.ts @@ -532,23 +532,25 @@ export const ConvertController = { to: toTokenAddress, userAddress: fromCaipAddress }) - const transaction = response.tx const gasLimit = await ConnectionController.getEstimatedGas({ address: fromAddress as `0x${string}`, - to: transaction.to as `0x${string}`, - data: transaction.data + to: CoreHelperUtil.getPlainAddress(response.tx.to) as `0x${string}`, + data: response.tx.data }) const toAmount = this.getToAmount() - return { - ...transaction, + const transaction = { + data: response.tx.data, + to: CoreHelperUtil.getPlainAddress(response.tx.from), gas: gasLimit, - gasPrice: BigInt(transaction.eip155?.gasPrice), - value: BigInt(transaction.value), + gasPrice: BigInt(response.tx.eip155.gasPrice), + value: BigInt(response.tx.value), toAmount } + + return transaction }, async sendTransactionForApproval(data: TransactionParams) { @@ -617,7 +619,7 @@ export const ConvertController = { const transaction = { data: response.tx.data, - to: response.tx.to, + to: CoreHelperUtil.getPlainAddress(response.tx.to), gas, gasPrice, value: BigInt(amount), @@ -650,6 +652,7 @@ export const ConvertController = { }) try { + console.log('>>> data', data, fromAddress) const transactionHash = await ConnectionController.sendTransaction({ address: fromAddress as `0x${string}`, to: data.to as `0x${string}`, @@ -670,6 +673,7 @@ export const ConvertController = { const error = err as TransactionError state.transactionError = error?.shortMessage state.transactionLoading = false + console.log('>>> error', err) SnackController.showError(error?.shortMessage || 'Transaction error') return undefined diff --git a/packages/core/src/utils/TypeUtil.ts b/packages/core/src/utils/TypeUtil.ts index d16c0e420b..7fc5353458 100644 --- a/packages/core/src/utils/TypeUtil.ts +++ b/packages/core/src/utils/TypeUtil.ts @@ -218,8 +218,8 @@ export interface BlockchainApiGenerateConvertCalldataRequest { export interface BlockchainApiGenerateConvertCalldataResponse { tx: { - from: string - to: `0x${string}` + from: `${string}:${string}:${string}` + to: `${string}:${string}:${string}` data: `0x${string}` amount: string eip155: { @@ -239,8 +239,8 @@ export interface BlockchainApiGenerateApproveCalldataRequest { export interface BlockchainApiGenerateApproveCalldataResponse { tx: { - from: string - to: `0x${string}` + from: `${string}:${string}:${string}` + to: `${string}:${string}:${string}` data: `0x${string}` value: string eip155: { From 246b340688b53c459d130c4a685e4a10bf4e8010 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Fri, 19 Apr 2024 18:22:26 +0300 Subject: [PATCH 85/96] refactor: blockchain api switching done --- .../controllers/BlockchainApiController.ts | 47 ++++++- .../core/src/controllers/ConvertController.ts | 73 ++++------ packages/core/src/utils/ConvertApiUtil.ts | 130 +++--------------- packages/core/src/utils/TypeUtil.ts | 26 +++- .../w3m-account-tokens-widget/index.ts | 2 +- 5 files changed, 115 insertions(+), 163 deletions(-) diff --git a/packages/core/src/controllers/BlockchainApiController.ts b/packages/core/src/controllers/BlockchainApiController.ts index 1e57ba95a3..bb520be0a3 100644 --- a/packages/core/src/controllers/BlockchainApiController.ts +++ b/packages/core/src/controllers/BlockchainApiController.ts @@ -10,6 +10,10 @@ import type { BlockchainApiGenerateConvertCalldataResponse, BlockchainApiGenerateApproveCalldataRequest, BlockchainApiGenerateApproveCalldataResponse, + BlockchainApiConvertAllowanceRequest, + BlockchainApiConvertAllowanceResponse, + BlockchainApiGasPriceRequest, + BlockchainApiGasPriceResponse, BlockchainApiTokenPriceRequest, BlockchainApiTokenPriceResponse, BlockchainApiIdentityRequest, @@ -140,14 +144,44 @@ export const BlockchainApiController = { fetchTokenPrice({ projectId, addresses }: BlockchainApiTokenPriceRequest) { return api.post({ - path: `/v1/fungible/price`, + path: '/v1/fungible/price', body: { projectId, currency: 'usd', addresses }, headers: { - 'content-type': 'text/json' + 'Content-Type': 'application/json' + } + }) + }, + + fetchConvertAllowance({ + projectId, + tokenAddress, + userAddress + }: BlockchainApiConvertAllowanceRequest) { + const { sdkType, sdkVersion } = OptionsController.state + + return api.get({ + path: `/v1/convert/allowance?projectId=${projectId}&tokenAddress=${tokenAddress}&userAddress=${userAddress}`, + headers: { + 'Content-Type': 'application/json', + 'x-sdk-type': sdkType, + 'x-sdk-version': sdkVersion + } + }) + }, + + fetchGasPrice({ projectId, chainId }: BlockchainApiGasPriceRequest) { + const { sdkType, sdkVersion } = OptionsController.state + + return api.get({ + path: `/v1/convert/gas-price?projectId=${projectId}&chainId=${chainId}`, + headers: { + 'Content-Type': 'application/json', + 'x-sdk-type': sdkType, + 'x-sdk-version': sdkVersion } }) }, @@ -178,16 +212,19 @@ export const BlockchainApiController = { }, generateApproveCalldata({ - amount, from, projectId, to, userAddress }: BlockchainApiGenerateApproveCalldataRequest) { + const { sdkType, sdkVersion } = OptionsController.state + return api.get({ - path: `/v1/convert/build-approve?projectId=${projectId}&userAddress=${userAddress}&from=${from}&amount=${amount}&to=${to}`, + path: `/v1/convert/build-approve?projectId=${projectId}&userAddress=${userAddress}&from=${from}&to=${to}`, headers: { - 'Content-Type': 'application/json' + 'Content-Type': 'application/json', + 'x-sdk-type': sdkType, + 'x-sdk-version': sdkVersion } }) }, diff --git a/packages/core/src/controllers/ConvertController.ts b/packages/core/src/controllers/ConvertController.ts index 810a034054..054f5d03ce 100644 --- a/packages/core/src/controllers/ConvertController.ts +++ b/packages/core/src/controllers/ConvertController.ts @@ -13,6 +13,7 @@ import { CoreHelperUtil } from '../utils/CoreHelperUtil.js' import { BlockchainApiController } from './BlockchainApiController.js' import { OptionsController } from './OptionsController.js' +// -- Constants ---------------------------------------- // export const INITIAL_GAS_LIMIT = 150000 // -- Types --------------------------------------------- // @@ -88,15 +89,6 @@ export interface TokenInfo { tags?: string[] } -export interface TokenInfoWithPrice extends TokenInfo { - price: string -} - -export interface TokenInfoWithBalance extends TokenInfo { - balance: string - price: string -} - type StateKey = keyof ConvertControllerState // -- State --------------------------------------------- // @@ -223,7 +215,7 @@ export const ConvertController = { }, async setTokenValues(address: string, target: 'sourceToken' | 'toToken') { - let price = state.tokensPriceMap[`${NetworkController.state.caipNetwork?.id}:${address}`] || 0 + let price = state.tokensPriceMap[address] || 0 if (!price) { price = await this.getAddressPrice(address) @@ -329,14 +321,15 @@ export const ConvertController = { return existPrice } // TODO: replace with blockchain api - // const response = await BlockchainApiController.fetchTokenPrice({ - // projectId: OptionsController.state.projectId, - // addresses: [address] - // }) - // const prices = response.fungibles - // const price = prices.find(p => p.address === address)?.price || '0' - const prices = await ConvertApiUtil.getTokenPriceWithAddresses([address]) - const price = prices[address] || '0' + const response = await BlockchainApiController.fetchTokenPrice({ + projectId: OptionsController.state.projectId, + addresses: [address] + }) + const fungibles = response.fungibles || [] + const alltokens = [...(state.tokens || []), ...(state.myTokensWithBalance || [])] + const symbol = alltokens?.find(t => t.address === address)?.symbol + // TODO: expect blockchain API to return address in fungibles objects + const price = fungibles.find(p => p.symbol === symbol)?.price || '0' state.tokensPriceMap[address] = parseFloat(price) return parseFloat(price) @@ -345,17 +338,12 @@ export const ConvertController = { async getNetworkTokenPrice() { const { networkAddress } = this.getParams() - // TODO: replace with blockchain api - // const response = await BlockchainApiController.fetchTokenPrice({ - // projectId: OptionsController.state.projectId, - // addresses: [networkAddress] - // }) - // const prices = response.fungibles - // const price = prices.find(p => p.address === networkAddress)?.price || '0' - const prices = await ConvertApiUtil.getTokenPriceWithAddresses([ - ConstantsUtil.NATIVE_TOKEN_ADDRESS - ]) - const price = prices[networkAddress] || '0' + const response = await BlockchainApiController.fetchTokenPrice({ + projectId: OptionsController.state.projectId, + addresses: [networkAddress] + }) + const token = response.fungibles?.[0] + const price = token?.price || '0' state.tokensPriceMap[networkAddress] = parseFloat(price) state.networkPrice = price }, @@ -382,9 +370,8 @@ export const ConvertController = { }, async getInitialGasPrice() { - const res = await ConvertApiUtil.getGasPrice() - const instant = res.instant - const value = typeof instant === 'object' ? res.instant.maxFeePerGas : instant + const res = await ConvertApiUtil.fetchGasPrice() + const value = res.instant const gasFee = BigInt(value) const gasLimit = BigInt(INITIAL_GAS_LIMIT) const gasPrice = this.calculateGasPriceInUSD(gasLimit, gasFee) @@ -463,7 +450,7 @@ export const ConvertController = { }, async getTransaction() { - const { fromAddress, sourceTokenAddress, sourceTokenAmount, sourceTokenDecimals } = + const { fromCaipAddress, sourceTokenAddress, sourceTokenAmount, sourceTokenDecimals } = this.getParams() if ( @@ -476,9 +463,9 @@ export const ConvertController = { } // TODO: replace with blockchain api - const hasAllowance = await ConvertApiUtil.checkConvertAllowance({ - fromAddress, - sourceTokenAddress: CoreHelperUtil.getPlainAddress(sourceTokenAddress) as string, + const hasAllowance = await ConvertApiUtil.fetchConvertAllowance({ + userAddress: fromCaipAddress, + tokenAddress: sourceTokenAddress, sourceTokenAmount, sourceTokenDecimals }) @@ -514,8 +501,7 @@ export const ConvertController = { }, async createTokenAllowance() { - const { fromCaipAddress, fromAddress, sourceTokenAddress, sourceTokenAmount, toTokenAddress } = - this.getParams() + const { fromCaipAddress, fromAddress, sourceTokenAddress, toTokenAddress } = this.getParams() if (!fromCaipAddress || !toTokenAddress) { return undefined @@ -528,7 +514,6 @@ export const ConvertController = { const response = await BlockchainApiController.generateApproveCalldata({ projectId: OptionsController.state.projectId, from: sourceTokenAddress, - amount: parseInt(sourceTokenAmount), // Todo: this is not used right now but we should update find the best way/format to set it to: toTokenAddress, userAddress: fromCaipAddress }) @@ -601,9 +586,10 @@ export const ConvertController = { } try { - const amount = parseInt( - ConnectionController.parseUnits(sourceTokenAmount, sourceTokenDecimals).toString() - ) + const amount = ConnectionController.parseUnits( + sourceTokenAmount, + sourceTokenDecimals + ).toString() const response = await BlockchainApiController.generateConvertCalldata({ projectId: OptionsController.state.projectId, @@ -614,7 +600,7 @@ export const ConvertController = { }) const toAmount = this.getToAmount() - const gas = BigInt(parseInt(response.tx.eip155.gas)) + const gas = BigInt(response.tx.eip155.gas) const gasPrice = BigInt(response.tx.eip155.gasPrice) const transaction = { @@ -652,7 +638,6 @@ export const ConvertController = { }) try { - console.log('>>> data', data, fromAddress) const transactionHash = await ConnectionController.sendTransaction({ address: fromAddress as `0x${string}`, to: data.to as `0x${string}`, diff --git a/packages/core/src/utils/ConvertApiUtil.ts b/packages/core/src/utils/ConvertApiUtil.ts index ad0f5e8a20..8d8c5efbba 100644 --- a/packages/core/src/utils/ConvertApiUtil.ts +++ b/packages/core/src/utils/ConvertApiUtil.ts @@ -7,6 +7,7 @@ import { ConstantsUtil } from '../utils/ConstantsUtil.js' import { BlockchainApiController } from '../controllers/BlockchainApiController.js' import type { ConvertTokenWithBalance } from './TypeUtil.js' import { OptionsController } from '../controllers/OptionsController.js' +import type { BlockchainApiConvertAllowanceRequest } from '../utils/TypeUtil.js' const ONEINCH_API_BASE_URL = 'https://1inch-swap-proxy.walletconnect-v1-bridge.workers.dev' @@ -39,46 +40,6 @@ export type TokenInfo = { tags?: string[] } -export interface TokenInfoWithPrice extends TokenInfo { - price: string -} - -export interface TokenInfoWithBalance extends TokenInfo { - balance: string - price: string -} - -export type SwapApprovalData = { - data: `0x${string}` - to: `0x${string}` - gasPrice: string - value: string -} - -export type TokenList = { - tokens: Record -} - -export type GetAllowanceParams = { - fromAddress: string - sourceTokenAddress: string - sourceTokenAmount: string - sourceTokenDecimals: number -} - -export type GetApprovalParams = { - sourceTokenAddress: string - sourceTokenAmount?: string -} - -export type GetConvertDataParams = { - sourceTokenAddress: string - toTokenAddress: string - sourceTokenAmount: string - fromAddress: string - decimals: number -} - export type TransactionData = { from: string to: `0x${string}` @@ -88,31 +49,6 @@ export type TransactionData = { gasPrice: string } -export type GetConvertDataResponse = { - toAmount: string - tx: TransactionData -} - -export type GetGasPricesResponse = { - baseFree: string - low: { - maxPriorityFeePerGas: string - maxFeePerGas: string - } - medium: { - maxPriorityFeePerGas: string - maxFeePerGas: string - } - high: { - maxPriorityFeePerGas: string - maxFeePerGas: string - } - instant: { - maxPriorityFeePerGas: string - maxFeePerGas: string - } -} - // -- Controller ---------------------------------------- // export const ConvertApiUtil = { get1InchAPI() { @@ -141,7 +77,7 @@ export const ConvertApiUtil = { }, async getTokenList() { - const response = await await BlockchainApiController.fetchConvertTokens({ + const response = await BlockchainApiController.fetchConvertTokens({ chainId: NetworkController.state.caipNetwork?.id, projectId: OptionsController.state.projectId }) @@ -162,33 +98,31 @@ export const ConvertApiUtil = { return tokens }, - async getGasPrice() { - const { api, paths } = this.get1InchAPI() - - const gasPrices = await api.get({ - path: paths.gasPrice, - headers: { 'content-type': 'application/json' } + async fetchGasPrice() { + return await BlockchainApiController.fetchGasPrice({ + projectId: OptionsController.state.projectId, + chainId: NetworkController.state.caipNetwork?.id }) - - return gasPrices }, - async checkConvertAllowance({ - fromAddress, - sourceTokenAddress, + async fetchConvertAllowance({ + tokenAddress, + userAddress, sourceTokenAmount, sourceTokenDecimals - }: GetAllowanceParams) { - const { api, paths } = this.get1InchAPI() - - const res = await api.get<{ allowance: string }>({ - path: paths.approveAllowance, - params: { tokenAddress: sourceTokenAddress, walletAddress: fromAddress } + }: Pick & { + sourceTokenAmount: string + sourceTokenDecimals: number + }) { + const response = await BlockchainApiController.fetchConvertAllowance({ + projectId: OptionsController.state.projectId, + tokenAddress: tokenAddress, + userAddress: userAddress }) - if (res?.allowance && sourceTokenAmount && sourceTokenDecimals) { + if (response?.allowance && sourceTokenAmount && sourceTokenDecimals) { const parsedValue = ConnectionController.parseUnits(sourceTokenAmount, sourceTokenDecimals) - const hasAllowance = BigInt(res.allowance) >= parsedValue + const hasAllowance = BigInt(response.allowance) >= parsedValue return hasAllowance } @@ -208,7 +142,7 @@ export const ConvertApiUtil = { return { symbol: token.symbol, name: token.name, - address: !!token.address + address: !!token?.address ? token.address : `${NetworkController.state.caipNetwork?.id}:${ConstantsUtil.NATIVE_TOKEN_ADDRESS}`, decimals: parseInt(token.quantity.decimals), @@ -240,29 +174,5 @@ export const ConvertApiUtil = { return _values }, {}) - }, - - mergeTokensWithBalanceAndPrice( - tokens: Record, - balances: Record, - tokensPrice: Record - ) { - const mergedTokens = Object.entries(tokens).reduce>( - (_mergedTokens, [tokenAddress, tokenInfo]) => { - _mergedTokens[tokenAddress] = { - ...tokenInfo, - balance: ConnectionController.formatUnits( - BigInt(balances[tokenAddress] ?? '0'), - tokenInfo.decimals - ), - price: tokensPrice[tokenAddress] ?? '0' - } - - return _mergedTokens - }, - {} - ) - - return mergedTokens } } diff --git a/packages/core/src/utils/TypeUtil.ts b/packages/core/src/utils/TypeUtil.ts index d31611ec59..697937ab5a 100644 --- a/packages/core/src/utils/TypeUtil.ts +++ b/packages/core/src/utils/TypeUtil.ts @@ -195,7 +195,6 @@ export interface BlockchainApiTokenPriceRequest { export interface BlockchainApiTokenPriceResponse { fungibles: { - address: string name: string symbol: string iconUrl: string @@ -203,12 +202,33 @@ export interface BlockchainApiTokenPriceResponse { }[] } +export interface BlockchainApiConvertAllowanceRequest { + projectId: string + tokenAddress: string + userAddress: string +} + +export interface BlockchainApiConvertAllowanceResponse { + allowance: string +} + +export interface BlockchainApiGasPriceRequest { + projectId: string + chainId: string +} + +export interface BlockchainApiGasPriceResponse { + standard: string + fast: string + instant: string +} + export interface BlockchainApiGenerateConvertCalldataRequest { projectId: string userAddress: string from: string to: string - amount: number + amount: string eip155?: { slippage: string permit?: string @@ -233,7 +253,7 @@ export interface BlockchainApiGenerateApproveCalldataRequest { userAddress: string from: string to: string - amount: number + amount?: number } export interface BlockchainApiGenerateApproveCalldataResponse { diff --git a/packages/scaffold/src/partials/w3m-account-tokens-widget/index.ts b/packages/scaffold/src/partials/w3m-account-tokens-widget/index.ts index 7263a53a50..2b5f527046 100644 --- a/packages/scaffold/src/partials/w3m-account-tokens-widget/index.ts +++ b/packages/scaffold/src/partials/w3m-account-tokens-widget/index.ts @@ -40,7 +40,7 @@ export class W3mAccountTokensWidget extends LitElement { // -- Private ------------------------------------------- // private watchConvertValues() { - this.watchTokenBalance = setInterval(() => AccountController.fetchTokenBalance(), 1000) + this.watchTokenBalance = setInterval(() => AccountController.fetchTokenBalance(), 10000) } private tokenTemplate() { From 7b1706a555e923fa08a025ea678172a65ed5817b Mon Sep 17 00:00:00 2001 From: enesozturk Date: Fri, 19 Apr 2024 19:23:00 +0300 Subject: [PATCH 86/96] refactor: remove 1inch endpoints, linter issues and general refactor --- packages/common/src/utils/TypeUtil.ts | 2 +- .../core/src/controllers/AccountController.ts | 3 + .../core/src/controllers/ConvertController.ts | 46 ++++--- packages/core/src/utils/ConvertApiUtil.ts | 122 +++++------------- .../w3m-account-tokens-widget/index.ts | 8 -- .../index.ts | 12 ++ .../views/w3m-convert-preview-view/index.ts | 1 - 7 files changed, 79 insertions(+), 115 deletions(-) diff --git a/packages/common/src/utils/TypeUtil.ts b/packages/common/src/utils/TypeUtil.ts index 832d148adb..a5fbead624 100644 --- a/packages/common/src/utils/TypeUtil.ts +++ b/packages/common/src/utils/TypeUtil.ts @@ -77,7 +77,7 @@ export interface Balance { name: string symbol: string chainId: string - address: string + address?: string value?: number price: number quantity: BalanceQuantity diff --git a/packages/core/src/controllers/AccountController.ts b/packages/core/src/controllers/AccountController.ts index e6dacaeef6..7a0ee2199d 100644 --- a/packages/core/src/controllers/AccountController.ts +++ b/packages/core/src/controllers/AccountController.ts @@ -5,6 +5,8 @@ import type { CaipAddress, ConnectedWalletInfo } from '../utils/TypeUtil.js' import type { Balance } from '@web3modal/common' import { BlockchainApiController } from './BlockchainApiController.js' import { SnackController } from './SnackController.js' +import { ConvertController } from './ConvertController.js' +import { ConvertApiUtil } from '../utils/ConvertApiUtil.js' // -- Types --------------------------------------------- // export interface AccountControllerState { @@ -97,6 +99,7 @@ export const AccountController = { const response = await BlockchainApiController.getBalance(state.address) this.setTokenBalance(response.balances) + ConvertController.setBalances(ConvertApiUtil.mapBalancesToConvertTokens(response.balances)) } } catch (error) { SnackController.showError('Failed to fetch token balance') diff --git a/packages/core/src/controllers/ConvertController.ts b/packages/core/src/controllers/ConvertController.ts index 054f5d03ce..73dfb8fc79 100644 --- a/packages/core/src/controllers/ConvertController.ts +++ b/packages/core/src/controllers/ConvertController.ts @@ -251,10 +251,10 @@ export const ConvertController = { const networkToken = state.tokens?.find(token => token.address === networkAddress) this.setSourceToken(networkToken) - state.toToken = undefined + state.sourceTokenPriceInUSD = state.tokensPriceMap[networkAddress] || 0 state.sourceTokenAmount = '0' + state.toToken = undefined state.toTokenAmount = '0' - state.sourceTokenPriceInUSD = 0 state.toTokenPriceInUSD = 0 state.gasPriceInUSD = 0 }, @@ -317,22 +317,24 @@ export const ConvertController = { async getAddressPrice(address: string) { const existPrice = state.tokensPriceMap[address] + if (existPrice) { return existPrice } - // TODO: replace with blockchain api + const response = await BlockchainApiController.fetchTokenPrice({ projectId: OptionsController.state.projectId, addresses: [address] }) const fungibles = response.fungibles || [] - const alltokens = [...(state.tokens || []), ...(state.myTokensWithBalance || [])] - const symbol = alltokens?.find(t => t.address === address)?.symbol - // TODO: expect blockchain API to return address in fungibles objects + const allTokens = [...(state.tokens || []), ...(state.myTokensWithBalance || [])] + const symbol = allTokens?.find(token => token.address === address)?.symbol const price = fungibles.find(p => p.symbol === symbol)?.price || '0' - state.tokensPriceMap[address] = parseFloat(price) + const priceAsFloat = parseFloat(price) + + state.tokensPriceMap[address] = priceAsFloat - return parseFloat(price) + return priceAsFloat }, async getNetworkTokenPrice() { @@ -349,7 +351,6 @@ export const ConvertController = { }, async getMyTokensWithBalance() { - const { networkAddress } = this.getParams() const balances = await ConvertApiUtil.getMyTokensWithBalance() if (!balances) { @@ -357,13 +358,18 @@ export const ConvertController = { } await this.getInitialGasPrice() + this.setBalances(balances) + }, + + setBalances(balances: ConvertTokenWithBalance[]) { + const { networkAddress } = this.getParams() const networkToken = balances.find(token => token.address === networkAddress) balances.forEach(token => { state.tokensPriceMap[token.address] = token.price || 0 }) - state.myTokensWithBalance = balances as ConvertTokenWithBalance[] + state.myTokensWithBalance = balances state.networkBalanceInUSD = networkToken ? NumberUtil.multiply(networkToken.quantity.numeric, networkToken.price).toString() : '0' @@ -371,10 +377,16 @@ export const ConvertController = { async getInitialGasPrice() { const res = await ConvertApiUtil.fetchGasPrice() + + if (!res) { + return + } + const value = res.instant const gasFee = BigInt(value) const gasLimit = BigInt(INITIAL_GAS_LIMIT) const gasPrice = this.calculateGasPriceInUSD(gasLimit, gasFee) + state.gasPriceInUSD = gasPrice }, @@ -454,6 +466,7 @@ export const ConvertController = { this.getParams() if ( + !fromCaipAddress || !sourceTokenAddress || !sourceTokenAmount || parseFloat(sourceTokenAmount) === 0 || @@ -462,7 +475,6 @@ export const ConvertController = { return undefined } - // TODO: replace with blockchain api const hasAllowance = await ConvertApiUtil.fetchConvertAllowance({ userAddress: fromCaipAddress, tokenAddress: sourceTokenAddress, @@ -528,7 +540,7 @@ export const ConvertController = { const transaction = { data: response.tx.data, - to: CoreHelperUtil.getPlainAddress(response.tx.from), + to: CoreHelperUtil.getPlainAddress(response.tx.from) as `0x${string}`, gas: gasLimit, gasPrice: BigInt(response.tx.eip155.gasPrice), value: BigInt(response.tx.value), @@ -568,6 +580,7 @@ export const ConvertController = { async createConvert() { const { + networkAddress, fromCaipAddress, sourceTokenAddress, sourceTokenDecimals, @@ -599,17 +612,19 @@ export const ConvertController = { amount }) + const isSourceTokenIsNetworkToken = sourceTokenAddress === networkAddress + const toAmount = this.getToAmount() const gas = BigInt(response.tx.eip155.gas) const gasPrice = BigInt(response.tx.eip155.gasPrice) const transaction = { data: response.tx.data, - to: CoreHelperUtil.getPlainAddress(response.tx.to), + to: CoreHelperUtil.getPlainAddress(response.tx.to) as `0x${string}`, gas, gasPrice, - value: BigInt(amount), - toAmount: toAmount + value: isSourceTokenIsNetworkToken ? BigInt(amount) : BigInt('0'), + toAmount } state.gasPriceInUSD = this.calculateGasPriceInUSD(gas, gasPrice) @@ -658,7 +673,6 @@ export const ConvertController = { const error = err as TransactionError state.transactionError = error?.shortMessage state.transactionLoading = false - console.log('>>> error', err) SnackController.showError(error?.shortMessage || 'Transaction error') return undefined diff --git a/packages/core/src/utils/ConvertApiUtil.ts b/packages/core/src/utils/ConvertApiUtil.ts index 8d8c5efbba..fe7d323a62 100644 --- a/packages/core/src/utils/ConvertApiUtil.ts +++ b/packages/core/src/utils/ConvertApiUtil.ts @@ -1,31 +1,14 @@ -import { CoreHelperUtil } from '../utils/CoreHelperUtil.js' import { NetworkController } from '../controllers/NetworkController.js' -import { FetchUtil } from '../utils/FetchUtil.js' import { AccountController } from '../controllers/AccountController.js' import { ConnectionController } from '../controllers/ConnectionController.js' import { ConstantsUtil } from '../utils/ConstantsUtil.js' import { BlockchainApiController } from '../controllers/BlockchainApiController.js' import type { ConvertTokenWithBalance } from './TypeUtil.js' import { OptionsController } from '../controllers/OptionsController.js' -import type { BlockchainApiConvertAllowanceRequest } from '../utils/TypeUtil.js' - -const ONEINCH_API_BASE_URL = 'https://1inch-swap-proxy.walletconnect-v1-bridge.workers.dev' - -function get1InchEndpoints(chainId: number, address: string | undefined) { - return { - approveTransaction: `/swap/v5.2/${chainId}/approve/transaction`, - approveAllowance: `/swap/v5.2/${chainId}/approve/allowance`, - gas: `/gas-price/v1.5/${chainId}`, - gasPrice: `/gas-price/v1.5/${chainId}`, - swap: `/swap/v5.2/${chainId}/swap`, - tokens: `/swap/v5.2/${chainId}/tokens`, - tokensCustom: `/token/v1.2/${chainId}/custom`, - tokensPrices: `/price/v1.1/${chainId}`, - search: `/token/v1.2/${chainId}/search`, - balance: `/balance/v1.2/${chainId}/balances/${address}`, - quote: `/swap/v6.0/${chainId}/quote` - } -} +import type { + BlockchainApiConvertAllowanceRequest, + BlockchainApiBalanceResponse +} from '../utils/TypeUtil.js' // -- Types --------------------------------------------- // export type TokenInfo = { @@ -40,42 +23,8 @@ export type TokenInfo = { tags?: string[] } -export type TransactionData = { - from: string - to: `0x${string}` - data: `0x${string}` - value: string - gas: bigint - gasPrice: string -} - // -- Controller ---------------------------------------- // export const ConvertApiUtil = { - get1InchAPI() { - const api = new FetchUtil({ baseUrl: ONEINCH_API_BASE_URL }) - const chainId = CoreHelperUtil.getEvmChainId(NetworkController.state.caipNetwork?.id) - const { address } = AccountController.state - - const endpoints = get1InchEndpoints(chainId, address) - - return { - api, - paths: { - approveTransaction: endpoints.approveTransaction, - approveAllowance: endpoints.approveAllowance, - gas: endpoints.gasPrice, - gasPrice: endpoints.gasPrice, - swap: endpoints.swap, - tokens: endpoints.tokens, - tokensCustom: endpoints.tokensCustom, - tokenPrices: endpoints.tokensPrices, - search: endpoints.search, - balance: endpoints.balance, - quote: endpoints.quote - } - } - }, - async getTokenList() { const response = await BlockchainApiController.fetchConvertTokens({ chainId: NetworkController.state.caipNetwork?.id, @@ -99,9 +48,16 @@ export const ConvertApiUtil = { }, async fetchGasPrice() { + const projectId = OptionsController.state.projectId + const caipNetwork = NetworkController.state.caipNetwork + + if (!caipNetwork) { + return null + } + return await BlockchainApiController.fetchGasPrice({ - projectId: OptionsController.state.projectId, - chainId: NetworkController.state.caipNetwork?.id + projectId, + chainId: caipNetwork.id }) }, @@ -114,10 +70,12 @@ export const ConvertApiUtil = { sourceTokenAmount: string sourceTokenDecimals: number }) { + const projectId = OptionsController.state.projectId + const response = await BlockchainApiController.fetchConvertAllowance({ - projectId: OptionsController.state.projectId, - tokenAddress: tokenAddress, - userAddress: userAddress + projectId, + tokenAddress, + userAddress }) if (response?.allowance && sourceTokenAmount && sourceTokenDecimals) { @@ -131,21 +89,28 @@ export const ConvertApiUtil = { }, async getMyTokensWithBalance() { - const response = await BlockchainApiController.getBalance( - // @ts-ignore - AccountController.state.address, - NetworkController.state.caipNetwork?.id - ) + const address = AccountController.state.address + const caipNetwork = NetworkController.state.caipNetwork + + if (!address || !caipNetwork) { + return [] + } + + const response = await BlockchainApiController.getBalance(address, caipNetwork.id) const balances = response.balances - const tokens = balances.map(token => { + return this.mapBalancesToConvertTokens(balances) + }, + + mapBalancesToConvertTokens(balances: BlockchainApiBalanceResponse['balances']) { + return balances.map(token => { return { symbol: token.symbol, name: token.name, - address: !!token?.address + address: token?.address ? token.address : `${NetworkController.state.caipNetwork?.id}:${ConstantsUtil.NATIVE_TOKEN_ADDRESS}`, - decimals: parseInt(token.quantity.decimals), + decimals: parseInt(token.quantity.decimals, 10), logoUri: token.iconUrl, eip2612: false, quantity: token.quantity, @@ -153,26 +118,5 @@ export const ConvertApiUtil = { value: token.value } as ConvertTokenWithBalance }) - - return tokens - }, - - async getTokenPriceWithAddresses(addresses: string[]) { - const { api, paths } = this.get1InchAPI() - - const values = await api.post>({ - path: paths.tokenPrices, - body: { tokens: addresses, currency: 'USD' }, - headers: { - 'content-type': 'application/json' - } - }) - - // return same values but update the keys with `eip155:137:` prefix to mimic blockchain api response - return Object.entries(values).reduce>((_values, [key, value]) => { - _values[`${NetworkController.state.caipNetwork?.id}:${key}`] = value - - return _values - }, {}) } } diff --git a/packages/scaffold/src/partials/w3m-account-tokens-widget/index.ts b/packages/scaffold/src/partials/w3m-account-tokens-widget/index.ts index 2b5f527046..88a9a0f279 100644 --- a/packages/scaffold/src/partials/w3m-account-tokens-widget/index.ts +++ b/packages/scaffold/src/partials/w3m-account-tokens-widget/index.ts @@ -9,8 +9,6 @@ export class W3mAccountTokensWidget extends LitElement { public static override styles = styles // -- Members ------------------------------------------- // - @state() private watchTokenBalance?: NodeJS.Timeout - private unsubscribe: (() => void)[] = [] // -- State & Properties -------------------------------- // @@ -25,12 +23,10 @@ export class W3mAccountTokensWidget extends LitElement { }) ] ) - this.watchConvertValues() } public override disconnectedCallback() { this.unsubscribe.forEach(unsubscribe => unsubscribe()) - clearInterval(this.watchTokenBalance) } // -- Render -------------------------------------------- // @@ -39,10 +35,6 @@ export class W3mAccountTokensWidget extends LitElement { } // -- Private ------------------------------------------- // - private watchConvertValues() { - this.watchTokenBalance = setInterval(() => AccountController.fetchTokenBalance(), 10000) - } - private tokenTemplate() { if (this.tokenBalance && this.tokenBalance?.length > 0) { return html` diff --git a/packages/scaffold/src/partials/w3m-account-wallet-features-widget/index.ts b/packages/scaffold/src/partials/w3m-account-wallet-features-widget/index.ts index 44d7b824ab..b99fb2187f 100644 --- a/packages/scaffold/src/partials/w3m-account-wallet-features-widget/index.ts +++ b/packages/scaffold/src/partials/w3m-account-wallet-features-widget/index.ts @@ -19,6 +19,8 @@ export class W3mAccountWalletFeaturesWidget extends LitElement { public static override styles = styles // -- Members ------------------------------------------- // + @state() private watchTokenBalance?: NodeJS.Timeout + private unsubscribe: (() => void)[] = [] // -- State & Properties -------------------------------- // @@ -57,10 +59,16 @@ export class W3mAccountWalletFeaturesWidget extends LitElement { this.network = val.caipNetwork }) ) + this.watchConvertValues() } public override disconnectedCallback() { this.unsubscribe.forEach(unsubscribe => unsubscribe()) + clearInterval(this.watchTokenBalance) + } + + public override firstUpdated() { + AccountController.fetchTokenBalance() } // -- Render -------------------------------------------- // @@ -121,6 +129,10 @@ export class W3mAccountWalletFeaturesWidget extends LitElement { } // -- Private ------------------------------------------- // + private watchConvertValues() { + this.watchTokenBalance = setInterval(() => AccountController.fetchTokenBalance(), 10000) + } + private listContentTemplate() { if (this.currentTab === 0) { return html`` diff --git a/packages/scaffold/src/views/w3m-convert-preview-view/index.ts b/packages/scaffold/src/views/w3m-convert-preview-view/index.ts index c1f6f2a1e6..bcfcfe3c0a 100644 --- a/packages/scaffold/src/views/w3m-convert-preview-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-preview-view/index.ts @@ -72,7 +72,6 @@ export class W3mConvertPreviewView extends LitElement { this.toToken = newState.toToken this.transactionLoading = newState.transactionLoading this.gasPriceInUSD = newState.gasPriceInUSD - this.transactionLoading = newState.transactionLoading this.toTokenPriceInUSD = newState.toTokenPriceInUSD this.sourceTokenAmount = newState.sourceTokenAmount ?? '' this.toTokenAmount = newState.toTokenAmount ?? '' From 463d6748e39c43a92fbcdcc5982dc887d5e8f21a Mon Sep 17 00:00:00 2001 From: enesozturk Date: Fri, 19 Apr 2024 20:41:42 +0300 Subject: [PATCH 87/96] refactor: code review updates --- packages/core/index.ts | 5 ++++- .../core/src/controllers/ConvertController.ts | 4 +++- .../core/src/controllers/RouterController.ts | 3 ++- .../src/partials/w3m-convert-input/index.ts | 19 ++++++++++++------- .../src/views/w3m-convert-view/index.ts | 17 +++++------------ packages/solana/src/client.ts | 14 ++++++++++---- 6 files changed, 36 insertions(+), 26 deletions(-) diff --git a/packages/core/index.ts b/packages/core/index.ts index 3d3720acdb..a87c64cd55 100644 --- a/packages/core/index.ts +++ b/packages/core/index.ts @@ -56,7 +56,10 @@ export { TransactionsController } from './src/controllers/TransactionsController export type { TransactionsControllerState } from './src/controllers/TransactionsController.js' export { ConvertController } from './src/controllers/ConvertController.js' -export type { ConvertControllerState } from './src/controllers/ConvertController.js' +export type { + ConvertControllerState, + ConvertInputTarget +} from './src/controllers/ConvertController.js' export { SendController } from './src/controllers/SendController.js' export type { SendControllerState } from './src/controllers/SendController.js' diff --git a/packages/core/src/controllers/ConvertController.ts b/packages/core/src/controllers/ConvertController.ts index 73dfb8fc79..d370341c02 100644 --- a/packages/core/src/controllers/ConvertController.ts +++ b/packages/core/src/controllers/ConvertController.ts @@ -17,6 +17,8 @@ import { OptionsController } from './OptionsController.js' export const INITIAL_GAS_LIMIT = 150000 // -- Types --------------------------------------------- // +export type ConvertInputTarget = 'sourceToken' | 'toToken' + type TransactionParams = { data: string to: string @@ -214,7 +216,7 @@ export const ConvertController = { } }, - async setTokenValues(address: string, target: 'sourceToken' | 'toToken') { + async setTokenValues(address: string, target: ConvertInputTarget) { let price = state.tokensPriceMap[address] || 0 if (!price) { diff --git a/packages/core/src/controllers/RouterController.ts b/packages/core/src/controllers/RouterController.ts index 168025a416..3bc78779e5 100644 --- a/packages/core/src/controllers/RouterController.ts +++ b/packages/core/src/controllers/RouterController.ts @@ -1,6 +1,7 @@ import { subscribeKey as subKey } from 'valtio/vanilla/utils' import { proxy } from 'valtio/vanilla' import type { CaipNetwork, Connector, WcWallet } from '../utils/TypeUtil.js' +import type { ConvertInputTarget } from './ConvertController.js' // -- Types --------------------------------------------- // type TransactionAction = { @@ -56,7 +57,7 @@ export interface RouterControllerState { network?: CaipNetwork email?: string newEmail?: string - target?: 'sourceToken' | 'toToken' + target?: ConvertInputTarget } transactionStack: TransactionAction[] } diff --git a/packages/scaffold/src/partials/w3m-convert-input/index.ts b/packages/scaffold/src/partials/w3m-convert-input/index.ts index a0b5b43fdf..2536797f5f 100644 --- a/packages/scaffold/src/partials/w3m-convert-input/index.ts +++ b/packages/scaffold/src/partials/w3m-convert-input/index.ts @@ -1,6 +1,11 @@ import { html, LitElement } from 'lit' import { property } from 'lit/decorators.js' -import { EventsController, RouterController, type ConvertToken } from '@web3modal/core' +import { + EventsController, + RouterController, + type ConvertToken, + type ConvertInputTarget +} from '@web3modal/core' import { NumberUtil } from '@web3modal/common' import { UiHelperUtil, @@ -12,8 +17,6 @@ import styles from './styles.js' const MINIMUM_USD_VALUE_TO_CONVERT = 0.00005 -type Target = 'sourceToken' | 'toToken' - @customElement('w3m-convert-input') export class W3mConvertInput extends LitElement { public static override styles = [styles] @@ -31,15 +34,17 @@ export class W3mConvertInput extends LitElement { @property() public disabled?: boolean - @property() public target: Target = 'sourceToken' + @property() public target: ConvertInputTarget = 'sourceToken' @property() public token?: ConvertToken - @property() public onSetAmount: ((target: Target, value: string) => void) | null = null - - @property() public onSetMaxValue: ((target: Target, balance: string | undefined) => void) | null = + @property() public onSetAmount: ((target: ConvertInputTarget, value: string) => void) | null = null + @property() public onSetMaxValue: + | ((target: ConvertInputTarget, balance: string | undefined) => void) + | null = null + // -- Render -------------------------------------------- // public override render() { const marketValue = this.marketValue || '0' diff --git a/packages/scaffold/src/views/w3m-convert-view/index.ts b/packages/scaffold/src/views/w3m-convert-view/index.ts index bd68d8d670..98b16dff64 100644 --- a/packages/scaffold/src/views/w3m-convert-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-view/index.ts @@ -9,12 +9,11 @@ import { NetworkController, ModalController, ConstantsUtil, - type ConvertToken + type ConvertToken, + type ConvertInputTarget } from '@web3modal/core' import { NumberUtil } from '@web3modal/common' -type Target = 'sourceToken' | 'toToken' - @customElement('w3m-convert-view') export class W3mConvertView extends LitElement { public static override styles = styles @@ -81,12 +80,6 @@ export class W3mConvertView extends LitElement { ConvertController.resetValues() } }), - ConvertController.subscribeKey('sourceToken', newSourceToken => { - this.sourceToken = newSourceToken - }), - ConvertController.subscribeKey('toToken', newToToken => { - this.toToken = newToToken - }), ConvertController.subscribe(newState => { this.initialized = newState.initialized this.loading = newState.loading @@ -195,7 +188,7 @@ export class W3mConvertView extends LitElement { ` } - private templateTokenInput(target: Target, token?: ConvertToken) { + private templateTokenInput(target: ConvertInputTarget, token?: ConvertToken) { const myToken = ConvertController.state.myTokensWithBalance?.find( ct => ct?.address === token?.address ) @@ -220,7 +213,7 @@ export class W3mConvertView extends LitElement { >` } - private onSetMaxValue(target: Target, balance: string | undefined) { + private onSetMaxValue(target: ConvertInputTarget, balance: string | undefined) { const token = target === 'sourceToken' ? this.sourceToken : this.toToken const isNetworkToken = token?.address === ConstantsUtil.NATIVE_TOKEN_ADDRESS @@ -279,7 +272,7 @@ export class W3mConvertView extends LitElement { ` } - private handleChangeAmount(target: Target, value: string) { + private handleChangeAmount(target: ConvertInputTarget, value: string) { ConvertController.clearError() if (target === 'sourceToken') { ConvertController.setSourceTokenAmount(value) diff --git a/packages/solana/src/client.ts b/packages/solana/src/client.ts index 35dbfe94b4..926abb9091 100644 --- a/packages/solana/src/client.ts +++ b/packages/solana/src/client.ts @@ -142,14 +142,20 @@ export class Web3Modal extends Web3ModalScaffold { return signature as string }, - sendTransaction: async () => { - return await Promise.resolve('0x') - }, - getEstimatedGas: async () => { return await Promise.resolve(BigInt(0)) }, + // -- Transaction methods --------------------------------------------------- + /** + * + * These methods are supported only on `wagmi` and `ethers` since the Solana SDK does not support them in the same way. + * These function definition is to have a type parity between the clients. Currently not in use. + */ + sendTransaction: async () => { + return await Promise.resolve('0x') + }, + parseUnits: () => BigInt(0), formatUnits: () => '' From 458b9bc6cd4a11a884c00a74d6a2fb08a39a5cc2 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Fri, 19 Apr 2024 21:08:12 +0300 Subject: [PATCH 88/96] refactor: code review improvements --- .../src/controllers/ConnectionController.ts | 6 ++-- .../core/src/controllers/ConvertController.ts | 9 +++--- .../controllers/ConnectionController.test.ts | 4 +-- packages/ethers/src/client.ts | 2 +- .../views/w3m-convert-preview-view/index.ts | 18 ++++++----- .../w3m-convert-select-token-view/index.ts | 10 +++--- .../src/views/w3m-convert-view/index.ts | 4 +-- packages/solana/src/client.ts | 2 +- packages/ui/index.ts | 3 +- packages/ui/package.json | 1 + packages/ui/src/utils/Math.ts | 30 ----------------- packages/ui/src/utils/MathUtil.ts | 32 +++++++++++++++++++ packages/ui/src/utils/NumberUtil.ts | 23 ------------- packages/ui/tests/Math.test.ts | 13 ++++++++ packages/ui/tests/UiHelperUtil.test.ts | 11 +++++++ packages/wagmi/src/client.ts | 4 +-- 16 files changed, 88 insertions(+), 84 deletions(-) delete mode 100644 packages/ui/src/utils/Math.ts create mode 100644 packages/ui/src/utils/MathUtil.ts delete mode 100644 packages/ui/src/utils/NumberUtil.ts create mode 100644 packages/ui/tests/Math.test.ts create mode 100644 packages/ui/tests/UiHelperUtil.test.ts diff --git a/packages/core/src/controllers/ConnectionController.ts b/packages/core/src/controllers/ConnectionController.ts index 8fd178d112..9e2d70d4d1 100644 --- a/packages/core/src/controllers/ConnectionController.ts +++ b/packages/core/src/controllers/ConnectionController.ts @@ -23,7 +23,7 @@ export interface ConnectionControllerClient { disconnect: () => Promise signMessage: (message: string) => Promise sendTransaction: (args: SendTransactionArgs) => Promise<`0x${string}` | null> - getEstimatedGas: (args: EstimateGasTransactionArgs) => Promise + estimateGas: (args: EstimateGasTransactionArgs) => Promise parseUnits: (value: string, decimals: number) => bigint formatUnits: (value: bigint, decimals: number) => string connectExternal?: (options: ConnectExternalOptions) => Promise @@ -104,8 +104,8 @@ export const ConnectionController = { return this._getClient().sendTransaction(args) }, - async getEstimatedGas(args: EstimateGasTransactionArgs) { - return this._getClient().getEstimatedGas(args) + async estimateGas(args: EstimateGasTransactionArgs) { + return this._getClient().estimateGas(args) }, checkInstalled(ids?: string[]) { diff --git a/packages/core/src/controllers/ConvertController.ts b/packages/core/src/controllers/ConvertController.ts index d370341c02..edd0660dec 100644 --- a/packages/core/src/controllers/ConvertController.ts +++ b/packages/core/src/controllers/ConvertController.ts @@ -195,6 +195,9 @@ export const ConvertController = { const { sourceTokenAddress, sourceTokenAmount } = this.getParams() if (!toToken) { + state.toTokenAmount = '0' + state.toTokenPriceInUSD = 0 + return } @@ -255,9 +258,7 @@ export const ConvertController = { this.setSourceToken(networkToken) state.sourceTokenPriceInUSD = state.tokensPriceMap[networkAddress] || 0 state.sourceTokenAmount = '0' - state.toToken = undefined - state.toTokenAmount = '0' - state.toTokenPriceInUSD = 0 + this.setToToken(undefined) state.gasPriceInUSD = 0 }, @@ -532,7 +533,7 @@ export const ConvertController = { userAddress: fromCaipAddress }) - const gasLimit = await ConnectionController.getEstimatedGas({ + const gasLimit = await ConnectionController.estimateGas({ address: fromAddress as `0x${string}`, to: CoreHelperUtil.getPlainAddress(response.tx.to) as `0x${string}`, data: response.tx.data diff --git a/packages/core/tests/controllers/ConnectionController.test.ts b/packages/core/tests/controllers/ConnectionController.test.ts index 2090363284..719265e924 100644 --- a/packages/core/tests/controllers/ConnectionController.test.ts +++ b/packages/core/tests/controllers/ConnectionController.test.ts @@ -15,7 +15,7 @@ const client: ConnectionControllerClient = { }, disconnect: async () => Promise.resolve(), signMessage: async (message: string) => Promise.resolve(message), - getEstimatedGas: async () => Promise.resolve(BigInt(0)), + estimateGas: async () => Promise.resolve(BigInt(0)), connectExternal: async _id => Promise.resolve(), checkInstalled: _id => true, parseUnits: value => BigInt(value), @@ -29,7 +29,7 @@ const clientCheckInstalledSpy = vi.spyOn(client, 'checkInstalled') const partialClient: ConnectionControllerClient = { connectWalletConnect: async () => Promise.resolve(), disconnect: async () => Promise.resolve(), - getEstimatedGas: async () => Promise.resolve(BigInt(0)), + estimateGas: async () => Promise.resolve(BigInt(0)), signMessage: async (message: string) => Promise.resolve(message), parseUnits: value => BigInt(value), formatUnits: value => value.toString(), diff --git a/packages/ethers/src/client.ts b/packages/ethers/src/client.ts index 4ff89bf0f8..698682c245 100644 --- a/packages/ethers/src/client.ts +++ b/packages/ethers/src/client.ts @@ -280,7 +280,7 @@ export class Web3Modal extends Web3ModalScaffold { formatUnits: (value: bigint, decimals: number) => formatUnits(value, decimals), - async getEstimatedGas(data) { + async estimateGas(data) { const chainId = EthersStoreUtil.state.chainId const provider = EthersStoreUtil.state.provider const address = EthersStoreUtil.state.address diff --git a/packages/scaffold/src/views/w3m-convert-preview-view/index.ts b/packages/scaffold/src/views/w3m-convert-preview-view/index.ts index bcfcfe3c0a..f332c00861 100644 --- a/packages/scaffold/src/views/w3m-convert-preview-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-preview-view/index.ts @@ -1,4 +1,4 @@ -import { customElement, formatNumberToLocalString } from '@web3modal/ui' +import { UiHelperUtil, customElement } from '@web3modal/ui' import { LitElement, html } from 'lit' import styles from './styles.js' import { @@ -91,16 +91,18 @@ export class W3mConvertPreviewView extends LitElement { // -- Private ------------------------------------------- // private templateSwap() { - const sourceTokenText = `${formatNumberToLocalString(parseFloat(this.sourceTokenAmount))} ${this - .sourceToken?.symbol}` - const toTokenText = `${formatNumberToLocalString(parseFloat(this.toTokenAmount))} ${this.toToken - ?.symbol}` + const sourceTokenText = `${UiHelperUtil.formatNumberToLocalString( + parseFloat(this.sourceTokenAmount) + )} ${this.sourceToken?.symbol}` + const toTokenText = `${UiHelperUtil.formatNumberToLocalString( + parseFloat(this.toTokenAmount) + )} ${this.toToken?.symbol}` const sourceTokenValue = parseFloat(this.sourceTokenAmount) * this.sourceTokenPriceInUSD const toTokenValue = parseFloat(this.toTokenAmount) * this.toTokenPriceInUSD - (this.gasPriceInUSD || 0) - const sentPrice = formatNumberToLocalString(sourceTokenValue) - const receivePrice = formatNumberToLocalString(toTokenValue) + const sentPrice = UiHelperUtil.formatNumberToLocalString(sourceTokenValue) + const receivePrice = UiHelperUtil.formatNumberToLocalString(toTokenValue) return html` @@ -192,7 +194,7 @@ export class W3mConvertPreviewView extends LitElement { sourceTokenPrice=${this.sourceTokenPriceInUSD} toTokenSymbol=${this.toToken?.symbol} toTokenConvertedAmount=${toTokenConvertedAmount} - gasPriceInUSD=${formatNumberToLocalString(this.gasPriceInUSD, 3)} + gasPriceInUSD=${UiHelperUtil.formatNumberToLocalString(this.gasPriceInUSD, 3)} .priceImpact=${this.priceImpact} slippageRate=${ConstantsUtil.CONVERT_SLIPPAGE_TOLERANCE} .maxSlippage=${this.maxSlippage} diff --git a/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts b/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts index d433470e19..934818c4e1 100644 --- a/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-select-token-view/index.ts @@ -1,4 +1,4 @@ -import { customElement, interpolate } from '@web3modal/ui' +import { customElement, MathUtil } from '@web3modal/ui' import { LitElement, html } from 'lit' import styles from './styles.js' import { RouterController, ConvertController, type ConvertTokenWithBalance } from '@web3modal/core' @@ -213,11 +213,11 @@ export class W3mConvertSelectTokenView extends LitElement { container.style.setProperty( '--suggested-tokens-scroll--left-opacity', - interpolate([0, 100], [0, 1], container.scrollLeft).toString() + MathUtil.interpolate([0, 100], [0, 1], container.scrollLeft).toString() ) container.style.setProperty( '--suggested-tokens-scroll--right-opacity', - interpolate( + MathUtil.interpolate( [0, 100], [0, 1], container.scrollWidth - container.scrollLeft - container.offsetWidth @@ -234,11 +234,11 @@ export class W3mConvertSelectTokenView extends LitElement { container.style.setProperty( '--tokens-scroll--top-opacity', - interpolate([0, 100], [0, 1], container.scrollTop).toString() + MathUtil.interpolate([0, 100], [0, 1], container.scrollTop).toString() ) container.style.setProperty( '--tokens-scroll--bottom-opacity', - interpolate( + MathUtil.interpolate( [0, 100], [0, 1], container.scrollHeight - container.scrollTop - container.offsetHeight diff --git a/packages/scaffold/src/views/w3m-convert-view/index.ts b/packages/scaffold/src/views/w3m-convert-view/index.ts index 98b16dff64..4cb755b6c7 100644 --- a/packages/scaffold/src/views/w3m-convert-view/index.ts +++ b/packages/scaffold/src/views/w3m-convert-view/index.ts @@ -1,4 +1,4 @@ -import { customElement, formatNumberToLocalString } from '@web3modal/ui' +import { UiHelperUtil, customElement } from '@web3modal/ui' import { LitElement, html } from 'lit' import { state } from 'lit/decorators.js' import styles from './styles.js' @@ -208,7 +208,7 @@ export class W3mConvertView extends LitElement { .token=${token} .balance=${myToken?.quantity?.numeric} .price=${this.sourceTokenPriceInUSD} - .marketValue=${isNaN(value) ? '' : formatNumberToLocalString(value)} + .marketValue=${isNaN(value) ? '' : UiHelperUtil.formatNumberToLocalString(value)} .onSetMaxValue=${this.onSetMaxValue.bind(this)} >` } diff --git a/packages/solana/src/client.ts b/packages/solana/src/client.ts index 926abb9091..b89c6540c6 100644 --- a/packages/solana/src/client.ts +++ b/packages/solana/src/client.ts @@ -142,7 +142,7 @@ export class Web3Modal extends Web3ModalScaffold { return signature as string }, - getEstimatedGas: async () => { + estimateGas: async () => { return await Promise.resolve(BigInt(0)) }, diff --git a/packages/ui/index.ts b/packages/ui/index.ts index a38fb8ff48..8ca07bcc1e 100644 --- a/packages/ui/index.ts +++ b/packages/ui/index.ts @@ -74,8 +74,7 @@ export * from './src/layout/wui-flex/index.js' export * from './src/layout/wui-grid/index.js' export * from './src/layout/wui-separator/index.js' -export * from './src/utils/Math.js' -export * from './src/utils/NumberUtil.js' +export { MathUtil } from './src/utils/MathUtil.js' export { initializeTheming, setColorTheme, setThemeVariables } from './src/utils/ThemeUtil.js' export { UiHelperUtil } from './src/utils/UiHelperUtil.js' export { TransactionUtil } from './src/utils/TransactionUtil.js' diff --git a/packages/ui/package.json b/packages/ui/package.json index 2e9aab626b..085985ac62 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -12,6 +12,7 @@ "build:clean": "rm -rf dist", "build:ui": "tsc --build", "watch": "tsc --watch", + "test": "vitest run --dir tests --coverage.enabled --reporter=junit --coverage.reporter=json-summary --coverage.reporter=html", "typecheck": "tsc --noEmit", "lint": "eslint . --ext .js,.jsx,.ts,.tsx" }, diff --git a/packages/ui/src/utils/Math.ts b/packages/ui/src/utils/Math.ts deleted file mode 100644 index e9f5c615a2..0000000000 --- a/packages/ui/src/utils/Math.ts +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Interpolates a value from one range to another - * @param inputRange - number array of length 2 that represents the original range - * @param outputRange - number array of length 2 that represents the new range - * @param value - the value to interpolation - * @returns - */ -export function interpolate(inputRange: number[], outputRange: number[], value: number) { - if (inputRange.length !== 2 || outputRange.length !== 2) { - throw new Error('inputRange and outputRange must be an array of length 2') - } - - const originalRangeMin = inputRange[0] || 0 - const originalRangeMax = inputRange[1] || 0 - const newRangeMin = outputRange[0] || 0 - const newRangeMax = outputRange[1] || 0 - - if (value < originalRangeMin) { - return newRangeMin - } - if (value > originalRangeMax) { - return newRangeMax - } - - return ( - ((newRangeMax - newRangeMin) / (originalRangeMax - originalRangeMin)) * - (value - originalRangeMin) + - newRangeMin - ) -} diff --git a/packages/ui/src/utils/MathUtil.ts b/packages/ui/src/utils/MathUtil.ts new file mode 100644 index 0000000000..60ba26db42 --- /dev/null +++ b/packages/ui/src/utils/MathUtil.ts @@ -0,0 +1,32 @@ +export const MathUtil = { + /** + * Interpolates a value from one range to another + * @param inputRange - number array of length 2 that represents the original range + * @param outputRange - number array of length 2 that represents the new range + * @param value - the value to interpolation + * @returns + */ + interpolate(inputRange: number[], outputRange: number[], value: number) { + if (inputRange.length !== 2 || outputRange.length !== 2) { + throw new Error('inputRange and outputRange must be an array of length 2') + } + + const originalRangeMin = inputRange[0] || 0 + const originalRangeMax = inputRange[1] || 0 + const newRangeMin = outputRange[0] || 0 + const newRangeMax = outputRange[1] || 0 + + if (value < originalRangeMin) { + return newRangeMin + } + if (value > originalRangeMax) { + return newRangeMax + } + + return ( + ((newRangeMax - newRangeMin) / (originalRangeMax - originalRangeMin)) * + (value - originalRangeMin) + + newRangeMin + ) + } +} diff --git a/packages/ui/src/utils/NumberUtil.ts b/packages/ui/src/utils/NumberUtil.ts deleted file mode 100644 index db975b0979..0000000000 --- a/packages/ui/src/utils/NumberUtil.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Format the given number or string to human readable numbers with the given number of decimals - * @param value - The value to format. It could be a number or string. If it's a string, it will be parsed to a float then formatted. - * @param decimals - number of decimals after dot - * @returns - */ -export function formatNumberToLocalString(value: string | number | undefined, decimals = 2) { - if (value === undefined) { - return '0.00' - } - - if (typeof value === 'number') { - return value.toLocaleString('en-US', { - maximumFractionDigits: decimals, - minimumFractionDigits: decimals - }) - } - - return parseFloat(value).toLocaleString('en-US', { - maximumFractionDigits: decimals, - minimumFractionDigits: decimals - }) -} diff --git a/packages/ui/tests/Math.test.ts b/packages/ui/tests/Math.test.ts new file mode 100644 index 0000000000..7407666387 --- /dev/null +++ b/packages/ui/tests/Math.test.ts @@ -0,0 +1,13 @@ +import { describe, expect, it } from 'vitest' +import { MathUtil } from '../src/utils/MathUtil.js' + +// -- Tests -------------------------------------------------------------------- +describe('MathUtil', () => { + it('should interpolate as expected', () => { + const inputRange = [0, 100] + const outputRange = [0, 1] + const value = 50 + + expect(MathUtil.interpolate(inputRange, outputRange, value)).toEqual(0.5) + }) +}) diff --git a/packages/ui/tests/UiHelperUtil.test.ts b/packages/ui/tests/UiHelperUtil.test.ts new file mode 100644 index 0000000000..ca7238dcbe --- /dev/null +++ b/packages/ui/tests/UiHelperUtil.test.ts @@ -0,0 +1,11 @@ +import { describe, expect, it } from 'vitest' +import { UiHelperUtil } from '../src/utils/UiHelperUtil.js' + +// -- Tests -------------------------------------------------------------------- +describe('UiHelperUtil', () => { + it('should format the numbers as expected', () => { + expect(UiHelperUtil.formatNumberToLocalString(1000000)).toEqual('1,000,000.00') + expect(UiHelperUtil.formatNumberToLocalString(1000000, 3)).toEqual('1,000,000.000') + expect(UiHelperUtil.formatNumberToLocalString(100, 5)).toEqual('100.00000') + }) +}) diff --git a/packages/wagmi/src/client.ts b/packages/wagmi/src/client.ts index ad06367d8f..c5a0ccb8ad 100644 --- a/packages/wagmi/src/client.ts +++ b/packages/wagmi/src/client.ts @@ -173,7 +173,7 @@ export class Web3Modal extends Web3ModalScaffold { signMessage: async message => signMessage(this.wagmiConfig, { message }), - getEstimatedGas: async args => { + estimateGas: async args => { try { return await estimateGas(this.wagmiConfig, { account: args.address, @@ -200,8 +200,6 @@ export class Web3Modal extends Web3ModalScaffold { type: 'legacy' as const } - // eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error, @typescript-eslint/ban-ts-comment - // @ts-ignore await prepareTransactionRequest(this.wagmiConfig, txParams) const tx = await wagmiSendTransaction(this.wagmiConfig, txParams) From 885a2755f72fe36667d9bb35679f367dd5fd9161 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Fri, 19 Apr 2024 21:16:32 +0300 Subject: [PATCH 89/96] chore: estimate gas naming --- packages/scaffold/src/client.ts | 32 ++++++++++++++++---------------- packages/wagmi/src/client.ts | 4 ++-- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/packages/scaffold/src/client.ts b/packages/scaffold/src/client.ts index 79069b2034..46d461a3dc 100644 --- a/packages/scaffold/src/client.ts +++ b/packages/scaffold/src/client.ts @@ -85,22 +85,6 @@ export class Web3ModalScaffold { ModalController.close() } - public redirect(route: RouterControllerState['view']) { - RouterController.push(route) - } - - public popTransactionStack(cancel?: boolean) { - RouterController.popTransactionStack(cancel) - } - - public isOpen() { - return ModalController.state.open - } - - public isTransactionStackEmpty() { - return RouterController.state.transactionStack.length === 0 - } - public setLoading(loading: ModalControllerState['loading']) { ModalController.setLoading(loading) } @@ -160,6 +144,22 @@ export class Web3ModalScaffold { } // -- Protected ---------------------------------------------------------------- + protected redirect(route: RouterControllerState['view']) { + RouterController.push(route) + } + + protected popTransactionStack(cancel?: boolean) { + RouterController.popTransactionStack(cancel) + } + + protected isOpen() { + return ModalController.state.open + } + + protected isTransactionStackEmpty() { + return RouterController.state.transactionStack.length === 0 + } + protected setIsConnected: (typeof AccountController)['setIsConnected'] = isConnected => { AccountController.setIsConnected(isConnected) } diff --git a/packages/wagmi/src/client.ts b/packages/wagmi/src/client.ts index c5a0ccb8ad..909115034d 100644 --- a/packages/wagmi/src/client.ts +++ b/packages/wagmi/src/client.ts @@ -10,7 +10,7 @@ import { watchAccount, watchConnectors, waitForTransactionReceipt, - estimateGas, + estimateGas as wagmiEstimateGas, getAccount } from '@wagmi/core' import { mainnet } from 'viem/chains' @@ -175,7 +175,7 @@ export class Web3Modal extends Web3ModalScaffold { estimateGas: async args => { try { - return await estimateGas(this.wagmiConfig, { + return await wagmiEstimateGas(this.wagmiConfig, { account: args.address, to: args.to, data: args.data, From 5f6f129610911ed4b2d1b24709fe63839aff9639 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Fri, 19 Apr 2024 21:32:44 +0300 Subject: [PATCH 90/96] feat: add regex util for checking transactions --- packages/ethers/src/client.ts | 39 +++++++++++++++++++++--- packages/scaffold-utils/exports/index.ts | 1 + packages/scaffold-utils/src/RegexUtil.ts | 4 +++ packages/wagmi/src/client.ts | 4 +-- 4 files changed, 41 insertions(+), 7 deletions(-) create mode 100644 packages/scaffold-utils/src/RegexUtil.ts diff --git a/packages/ethers/src/client.ts b/packages/ethers/src/client.ts index 61aa7f5830..ce6512d4b0 100644 --- a/packages/ethers/src/client.ts +++ b/packages/ethers/src/client.ts @@ -12,7 +12,7 @@ import type { Token } from '@web3modal/scaffold' import { Web3ModalScaffold } from '@web3modal/scaffold' -import { ConstantsUtil, PresetsUtil, HelpersUtil } from '@web3modal/scaffold-utils' +import { ConstantsUtil, PresetsUtil, HelpersUtil, RegexUtil } from '@web3modal/scaffold-utils' import EthereumProvider from '@walletconnect/ethereum-provider' import type { Web3ModalSIWEClient } from '@web3modal/siwe' import type { @@ -822,13 +822,17 @@ export class Web3Modal extends Web3ModalScaffold { private watchEmail() { if (this.emailProvider) { this.emailProvider.onRpcRequest(request => { - // We only open the modal if it's not a safe (auto-approve) if (W3mFrameHelpers.checkIfRequestExists(request)) { if (!W3mFrameHelpers.checkIfRequestIsAllowed(request)) { - super.open({ view: 'ApproveTransaction' }) + if (super.isOpen()) { + if (!super.isTransactionStackEmpty()) { + super.redirect('ApproveTransaction') + } + } else { + super.open({ view: 'ApproveTransaction' }) + } } } else { - this.emailProvider?.rejectRpcRequest() super.open() const method = W3mFrameHelpers.getRequestMethod(request) // eslint-disable-next-line no-console @@ -839,7 +843,32 @@ export class Web3Modal extends Web3ModalScaffold { } }) this.emailProvider.onRpcResponse(() => { - super.close() + // eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error, @typescript-eslint/ban-ts-comment + // @ts-ignore + const payload = receive?.payload + // eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error, @typescript-eslint/ban-ts-comment + // @ts-ignore + const isError = receive?.type === '@w3m-frame/RPC_REQUEST_ERROR' + + if (isError && super.isOpen()) { + if (super.isTransactionStackEmpty()) { + super.close() + } else { + super.popTransactionStack(true) + } + } + + const isPayloadString = typeof payload === 'string' + const isAddress = isPayloadString ? payload?.startsWith('0x') : false + const isCompleted = isAddress && payload?.match(RegexUtil.transactionHashRegex) + + if (isCompleted) { + if (super.isTransactionStackEmpty()) { + super.close() + } else { + super.popTransactionStack() + } + } }) this.emailProvider.onNotConnected(() => { this.setIsConnected(false) diff --git a/packages/scaffold-utils/exports/index.ts b/packages/scaffold-utils/exports/index.ts index 6d93c98848..eb680fc93e 100644 --- a/packages/scaffold-utils/exports/index.ts +++ b/packages/scaffold-utils/exports/index.ts @@ -1,3 +1,4 @@ export { ConstantsUtil } from '../src/ConstantsUtil.js' export { PresetsUtil } from '../src/PresetsUtil.js' export { HelpersUtil } from '../src/HelpersUtil.js' +export { RegexUtil } from '../src/RegexUtil.js' diff --git a/packages/scaffold-utils/src/RegexUtil.ts b/packages/scaffold-utils/src/RegexUtil.ts new file mode 100644 index 0000000000..7528529363 --- /dev/null +++ b/packages/scaffold-utils/src/RegexUtil.ts @@ -0,0 +1,4 @@ +export const RegexUtil = { + addressRegex: /^0x([A-Fa-f0-9]{40})$/, + transactionHashRegex: /^0x([A-Fa-f0-9]{64})$/ +} diff --git a/packages/wagmi/src/client.ts b/packages/wagmi/src/client.ts index f688ebf99f..805c4248e2 100644 --- a/packages/wagmi/src/client.ts +++ b/packages/wagmi/src/client.ts @@ -33,7 +33,7 @@ import { formatUnits, parseUnits } from 'viem' import type { Hex } from 'viem' import { Web3ModalScaffold } from '@web3modal/scaffold' import type { Web3ModalSIWEClient } from '@web3modal/siwe' -import { ConstantsUtil, PresetsUtil, HelpersUtil } from '@web3modal/scaffold-utils' +import { ConstantsUtil, PresetsUtil, HelpersUtil, RegexUtil } from '@web3modal/scaffold-utils' import { getCaipDefaultChain, getEmailCaipNetworks, @@ -511,7 +511,7 @@ export class Web3Modal extends Web3ModalScaffold { const isPayloadString = typeof payload === 'string' const isAddress = isPayloadString ? payload?.startsWith('0x') : false - const isCompleted = isAddress && payload?.length > 10 + const isCompleted = isAddress && payload?.match(RegexUtil.transactionHashRegex) if (isCompleted) { if (super.isTransactionStackEmpty()) { From 204dc8ebc86ad1675d20b43755d149c0d17aba03 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Fri, 19 Apr 2024 21:44:16 +0300 Subject: [PATCH 91/96] chore: linter issues --- packages/scaffold-utils/src/RegexUtil.ts | 4 ++-- packages/ui/tsconfig.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/scaffold-utils/src/RegexUtil.ts b/packages/scaffold-utils/src/RegexUtil.ts index 7528529363..0cbacb89f3 100644 --- a/packages/scaffold-utils/src/RegexUtil.ts +++ b/packages/scaffold-utils/src/RegexUtil.ts @@ -1,4 +1,4 @@ export const RegexUtil = { - addressRegex: /^0x([A-Fa-f0-9]{40})$/, - transactionHashRegex: /^0x([A-Fa-f0-9]{64})$/ + addressRegex: /^0x(?:[A-Fa-f0-9]{40})$/u, + transactionHashRegex: /^0x(?:[A-Fa-f0-9]{64})$/u } diff --git a/packages/ui/tsconfig.json b/packages/ui/tsconfig.json index 62ac5c269e..cdb10021d9 100644 --- a/packages/ui/tsconfig.json +++ b/packages/ui/tsconfig.json @@ -5,5 +5,5 @@ "declarationDir": "./dist/types" }, "extends": "../../tsconfig.json", - "include": ["index.ts", "src"] + "include": ["index.ts", "src", "tests"] } From bf9e857997bef9eb373e2d6e78b1a91815c0f6dd Mon Sep 17 00:00:00 2001 From: enesozturk Date: Fri, 19 Apr 2024 21:46:03 +0300 Subject: [PATCH 92/96] fix: ethers onrpcresponse function param --- packages/ethers/src/client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ethers/src/client.ts b/packages/ethers/src/client.ts index ce6512d4b0..878f5480ba 100644 --- a/packages/ethers/src/client.ts +++ b/packages/ethers/src/client.ts @@ -842,7 +842,7 @@ export class Web3Modal extends Web3ModalScaffold { }, 300) } }) - this.emailProvider.onRpcResponse(() => { + this.emailProvider.onRpcResponse(receive => { // eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error, @typescript-eslint/ban-ts-comment // @ts-ignore const payload = receive?.payload From 418bc74ea7e0e1d67d6427339518457048528ee7 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Fri, 19 Apr 2024 22:01:55 +0300 Subject: [PATCH 93/96] chore: remove unused core method --- packages/core/src/utils/CoreHelperUtil.ts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/packages/core/src/utils/CoreHelperUtil.ts b/packages/core/src/utils/CoreHelperUtil.ts index c93d9dc78e..1f6533a3b5 100644 --- a/packages/core/src/utils/CoreHelperUtil.ts +++ b/packages/core/src/utils/CoreHelperUtil.ts @@ -54,16 +54,6 @@ export const CoreHelperUtil = { return caipAddress.split(':')[2] }, - getEvmChainId(caipNetworkId?: `${string}:${string}`) { - const strChainId = caipNetworkId?.split(':')?.[1] - if (!strChainId) { - // Default to Ethereum mainnet - return 1 - } - - return parseInt(strChainId, 10) - }, - async wait(milliseconds: number) { return new Promise(resolve => { setTimeout(resolve, milliseconds) From 05e017a057b0622169fac1fdc4b3bb932b2a2f39 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Sat, 20 Apr 2024 00:15:47 +0300 Subject: [PATCH 94/96] refactor: rpc response logics --- packages/ethers/src/client.ts | 57 +++++++++++++----------- packages/scaffold-utils/exports/index.ts | 1 - packages/scaffold-utils/src/RegexUtil.ts | 4 -- packages/wagmi/src/client.ts | 50 ++++++++++----------- packages/wallet/index.ts | 6 ++- packages/wallet/src/RegexUtil.ts | 4 ++ packages/wallet/src/W3mFrame.ts | 4 +- packages/wallet/src/W3mFrameConstants.ts | 13 ++++-- packages/wallet/src/W3mFrameHelpers.ts | 29 +++++++++++- 9 files changed, 105 insertions(+), 63 deletions(-) delete mode 100644 packages/scaffold-utils/src/RegexUtil.ts create mode 100644 packages/wallet/src/RegexUtil.ts diff --git a/packages/ethers/src/client.ts b/packages/ethers/src/client.ts index 878f5480ba..574d9c97b5 100644 --- a/packages/ethers/src/client.ts +++ b/packages/ethers/src/client.ts @@ -12,7 +12,7 @@ import type { Token } from '@web3modal/scaffold' import { Web3ModalScaffold } from '@web3modal/scaffold' -import { ConstantsUtil, PresetsUtil, HelpersUtil, RegexUtil } from '@web3modal/scaffold-utils' +import { ConstantsUtil, PresetsUtil, HelpersUtil } from '@web3modal/scaffold-utils' import EthereumProvider from '@walletconnect/ethereum-provider' import type { Web3ModalSIWEClient } from '@web3modal/siwe' import type { @@ -38,7 +38,12 @@ import { } from '@web3modal/scaffold-utils/ethers' import type { EthereumProviderOptions } from '@walletconnect/ethereum-provider' import type { Eip1193Provider } from 'ethers' -import { W3mFrameProvider, W3mFrameHelpers, W3mFrameRpcConstants } from '@web3modal/wallet' +import { + W3mFrameProvider, + W3mFrameHelpers, + W3mFrameRpcConstants, + W3mFrameConstants +} from '@web3modal/wallet' import type { CombinedProvider } from '@web3modal/scaffold-utils/ethers' import { BrowserProvider } from 'ethers' import { JsonRpcSigner } from 'ethers' @@ -842,32 +847,32 @@ export class Web3Modal extends Web3ModalScaffold { }, 300) } }) - this.emailProvider.onRpcResponse(receive => { - // eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error, @typescript-eslint/ban-ts-comment - // @ts-ignore - const payload = receive?.payload - // eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error, @typescript-eslint/ban-ts-comment - // @ts-ignore - const isError = receive?.type === '@w3m-frame/RPC_REQUEST_ERROR' - - if (isError && super.isOpen()) { - if (super.isTransactionStackEmpty()) { - super.close() - } else { - super.popTransactionStack(true) + this.emailProvider.onRpcResponse(response => { + const responseType = W3mFrameHelpers.getResponseType(response) + + switch (responseType) { + case W3mFrameConstants.RPC_RESPONSE_TYPE_ERROR: { + const isModalOpen = super.isOpen() + + if (isModalOpen) { + if (super.isTransactionStackEmpty()) { + super.close() + } else { + super.popTransactionStack(true) + } + } + break } - } - - const isPayloadString = typeof payload === 'string' - const isAddress = isPayloadString ? payload?.startsWith('0x') : false - const isCompleted = isAddress && payload?.match(RegexUtil.transactionHashRegex) - - if (isCompleted) { - if (super.isTransactionStackEmpty()) { - super.close() - } else { - super.popTransactionStack() + case W3mFrameConstants.RPC_RESPONSE_TYPE_TX: { + if (super.isTransactionStackEmpty()) { + super.close() + } else { + super.popTransactionStack() + } + break } + default: + break } }) this.emailProvider.onNotConnected(() => { diff --git a/packages/scaffold-utils/exports/index.ts b/packages/scaffold-utils/exports/index.ts index eb680fc93e..6d93c98848 100644 --- a/packages/scaffold-utils/exports/index.ts +++ b/packages/scaffold-utils/exports/index.ts @@ -1,4 +1,3 @@ export { ConstantsUtil } from '../src/ConstantsUtil.js' export { PresetsUtil } from '../src/PresetsUtil.js' export { HelpersUtil } from '../src/HelpersUtil.js' -export { RegexUtil } from '../src/RegexUtil.js' diff --git a/packages/scaffold-utils/src/RegexUtil.ts b/packages/scaffold-utils/src/RegexUtil.ts deleted file mode 100644 index 0cbacb89f3..0000000000 --- a/packages/scaffold-utils/src/RegexUtil.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const RegexUtil = { - addressRegex: /^0x(?:[A-Fa-f0-9]{40})$/u, - transactionHashRegex: /^0x(?:[A-Fa-f0-9]{64})$/u -} diff --git a/packages/wagmi/src/client.ts b/packages/wagmi/src/client.ts index 805c4248e2..6acde21c47 100644 --- a/packages/wagmi/src/client.ts +++ b/packages/wagmi/src/client.ts @@ -33,13 +33,13 @@ import { formatUnits, parseUnits } from 'viem' import type { Hex } from 'viem' import { Web3ModalScaffold } from '@web3modal/scaffold' import type { Web3ModalSIWEClient } from '@web3modal/siwe' -import { ConstantsUtil, PresetsUtil, HelpersUtil, RegexUtil } from '@web3modal/scaffold-utils' +import { ConstantsUtil, PresetsUtil, HelpersUtil } from '@web3modal/scaffold-utils' import { getCaipDefaultChain, getEmailCaipNetworks, getWalletConnectCaipNetworks } from './utils/helpers.js' -import { W3mFrameHelpers, W3mFrameRpcConstants } from '@web3modal/wallet' +import { W3mFrameConstants, W3mFrameHelpers, W3mFrameRpcConstants } from '@web3modal/wallet' import type { W3mFrameProvider, W3mFrameTypes } from '@web3modal/wallet' import { NetworkUtil } from '@web3modal/common' import type { defaultWagmiConfig as coreConfig } from './utils/defaultWagmiCoreConfig.js' @@ -493,32 +493,32 @@ export class Web3Modal extends Web3ModalScaffold { } }) - provider.onRpcResponse(receive => { - // eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error, @typescript-eslint/ban-ts-comment - // @ts-ignore - const payload = receive?.payload - // eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error, @typescript-eslint/ban-ts-comment - // @ts-ignore - const isError = receive?.type === '@w3m-frame/RPC_REQUEST_ERROR' - - if (isError && super.isOpen()) { - if (super.isTransactionStackEmpty()) { - super.close() - } else { - super.popTransactionStack(true) - } - } + provider.onRpcResponse(response => { + const responseType = W3mFrameHelpers.getResponseType(response) - const isPayloadString = typeof payload === 'string' - const isAddress = isPayloadString ? payload?.startsWith('0x') : false - const isCompleted = isAddress && payload?.match(RegexUtil.transactionHashRegex) + switch (responseType) { + case W3mFrameConstants.RPC_RESPONSE_TYPE_ERROR: { + const isModalOpen = super.isOpen() - if (isCompleted) { - if (super.isTransactionStackEmpty()) { - super.close() - } else { - super.popTransactionStack() + if (isModalOpen) { + if (super.isTransactionStackEmpty()) { + super.close() + } else { + super.popTransactionStack(true) + } + } + break + } + case W3mFrameConstants.RPC_RESPONSE_TYPE_TX: { + if (super.isTransactionStackEmpty()) { + super.close() + } else { + super.popTransactionStack() + } + break } + default: + break } }) diff --git a/packages/wallet/index.ts b/packages/wallet/index.ts index ab4b9012be..a89ce5c840 100644 --- a/packages/wallet/index.ts +++ b/packages/wallet/index.ts @@ -3,6 +3,10 @@ export { W3mFrame } from './src/W3mFrame.js' export { W3mFrameHelpers } from './src/W3mFrameHelpers.js' export { W3mFrameProvider } from './src/W3mFrameProvider.js' export { W3mFrameSchema } from './src/W3mFrameSchema.js' -export { W3mFrameConstants, W3mFrameRpcConstants } from './src/W3mFrameConstants.js' +export { + W3mFrameConstants, + W3mFrameRpcConstants, + type W3mFrameConstantValue +} from './src/W3mFrameConstants.js' export { W3mFrameStorage } from './src/W3mFrameStorage.js' export type { W3mFrameTypes } from './src/W3mFrameTypes.js' diff --git a/packages/wallet/src/RegexUtil.ts b/packages/wallet/src/RegexUtil.ts new file mode 100644 index 0000000000..72f5af9c68 --- /dev/null +++ b/packages/wallet/src/RegexUtil.ts @@ -0,0 +1,4 @@ +export const RegexUtil = { + address: /^0x(?:[A-Fa-f0-9]{40})$/u, + transactionHash: /^0x(?:[A-Fa-f0-9]{64})$/u +} diff --git a/packages/wallet/src/W3mFrame.ts b/packages/wallet/src/W3mFrame.ts index 9ee4933230..b71797bf51 100644 --- a/packages/wallet/src/W3mFrame.ts +++ b/packages/wallet/src/W3mFrame.ts @@ -1,4 +1,4 @@ -import { W3mFrameConstants } from './W3mFrameConstants.js' +import { SECURE_SITE_SDK, W3mFrameConstants } from './W3mFrameConstants.js' import { W3mFrameSchema } from './W3mFrameSchema.js' import { W3mFrameHelpers } from './W3mFrameHelpers.js' import type { W3mFrameTypes } from './W3mFrameTypes.js' @@ -34,7 +34,7 @@ export class W3mFrame { if (W3mFrameHelpers.isClient) { const iframe = document.createElement('iframe') iframe.id = 'w3m-iframe' - iframe.src = `${W3mFrameConstants.SECURE_SITE_SDK}?projectId=${projectId}` + iframe.src = `${SECURE_SITE_SDK}?projectId=${projectId}` iframe.style.position = 'fixed' iframe.style.zIndex = '999999' iframe.style.display = 'none' diff --git a/packages/wallet/src/W3mFrameConstants.ts b/packages/wallet/src/W3mFrameConstants.ts index e4e626cb91..df49e276d6 100644 --- a/packages/wallet/src/W3mFrameConstants.ts +++ b/packages/wallet/src/W3mFrameConstants.ts @@ -1,6 +1,7 @@ +export const SECURE_SITE_SDK = + process.env['NEXT_PUBLIC_SECURE_SITE_SDK_URL'] || 'https://secure.walletconnect.com/sdk' + export const W3mFrameConstants = { - SECURE_SITE_SDK: - process.env['NEXT_PUBLIC_SECURE_SITE_SDK_URL'] || 'https://secure.walletconnect.com/sdk', APP_EVENT_KEY: '@w3m-app/', FRAME_EVENT_KEY: '@w3m-frame/', RPC_METHOD_KEY: 'RPC_', @@ -70,9 +71,15 @@ export const W3mFrameConstants = { FRAME_INIT_SMART_ACCOUNT_SUCCESS: '@w3m-frame/INIT_SMART_ACCOUNT_SUCCESS', FRAME_INIT_SMART_ACCOUNT_ERROR: '@w3m-frame/INIT_SMART_ACCOUNT_ERROR', FRAME_SET_PREFERRED_ACCOUNT_SUCCESS: '@w3m-frame/SET_PREFERRED_ACCOUNT_SUCCESS', - FRAME_SET_PREFERRED_ACCOUNT_ERROR: '@w3m-frame/SET_PREFERRED_ACCOUNT_ERROR' + FRAME_SET_PREFERRED_ACCOUNT_ERROR: '@w3m-frame/SET_PREFERRED_ACCOUNT_ERROR', + + RPC_RESPONSE_TYPE_ERROR: 'RPC_RESPONSE_ERROR', + RPC_RESPONSE_TYPE_TX: 'RPC_RESPONSE_TRANSACTION_HASH', + RPC_RESPONSE_TYPE_OBJECT: 'RPC_RESPONSE_OBJECT' } as const +export type W3mFrameConstantValue = (typeof W3mFrameConstants)[keyof typeof W3mFrameConstants] + export const W3mFrameRpcConstants = { SAFE_RPC_METHODS: [ 'eth_accounts', diff --git a/packages/wallet/src/W3mFrameHelpers.ts b/packages/wallet/src/W3mFrameHelpers.ts index 845113d99c..a42758d441 100644 --- a/packages/wallet/src/W3mFrameHelpers.ts +++ b/packages/wallet/src/W3mFrameHelpers.ts @@ -1,6 +1,11 @@ import { W3mFrameStorage } from './W3mFrameStorage.js' -import { W3mFrameConstants, W3mFrameRpcConstants } from './W3mFrameConstants.js' +import { + W3mFrameConstants, + W3mFrameRpcConstants, + type W3mFrameConstantValue +} from './W3mFrameConstants.js' import type { W3mFrameTypes } from './W3mFrameTypes.js' +import { RegexUtil } from './RegexUtil.js' const RESTRICTED_TIMEZONES = [ 'ASIA/SHANGHAI', @@ -67,6 +72,28 @@ export const W3mFrameHelpers = { return (request as { payload: W3mFrameTypes.RPCRequest })?.payload?.method }, + getResponseType(response: unknown) { + const { type, payload } = response as { + type: W3mFrameConstantValue + payload: W3mFrameTypes.RPCResponse + } + + const isError = type === W3mFrameConstants.FRAME_RPC_REQUEST_ERROR + + if (isError) { + return W3mFrameConstants.RPC_RESPONSE_TYPE_ERROR + } + + const isPayloadString = typeof payload === 'string' + const isTransactionHash = isPayloadString && payload.match(RegexUtil.transactionHash) + + if (isTransactionHash) { + return W3mFrameConstants.RPC_RESPONSE_TYPE_TX + } + + return W3mFrameConstants.RPC_RESPONSE_TYPE_OBJECT + }, + checkIfRequestIsAllowed(request: unknown) { const method = this.getRequestMethod(request) From 3d47f7ea0cbb9da121d0a92738786bb2223e2509 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Mon, 22 Apr 2024 11:47:32 +0300 Subject: [PATCH 95/96] refactor: add long tx hash handling to rpc response handler --- packages/wallet/src/RegexUtil.ts | 3 ++- packages/wallet/src/W3mFrameHelpers.ts | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/wallet/src/RegexUtil.ts b/packages/wallet/src/RegexUtil.ts index 72f5af9c68..c3cdbab3eb 100644 --- a/packages/wallet/src/RegexUtil.ts +++ b/packages/wallet/src/RegexUtil.ts @@ -1,4 +1,5 @@ export const RegexUtil = { address: /^0x(?:[A-Fa-f0-9]{40})$/u, - transactionHash: /^0x(?:[A-Fa-f0-9]{64})$/u + transactionHash: /^0x(?:[A-Fa-f0-9]{64})$/u, + transactionHashLong: /^0x(?:[A-Fa-f0-9]{130})$/u } diff --git a/packages/wallet/src/W3mFrameHelpers.ts b/packages/wallet/src/W3mFrameHelpers.ts index a42758d441..fabc1c8d87 100644 --- a/packages/wallet/src/W3mFrameHelpers.ts +++ b/packages/wallet/src/W3mFrameHelpers.ts @@ -85,7 +85,9 @@ export const W3mFrameHelpers = { } const isPayloadString = typeof payload === 'string' - const isTransactionHash = isPayloadString && payload.match(RegexUtil.transactionHash) + const isTransactionHash = + isPayloadString && + (payload.match(RegexUtil.transactionHash) || payload.match(RegexUtil.transactionHashLong)) if (isTransactionHash) { return W3mFrameConstants.RPC_RESPONSE_TYPE_TX From b7ca5079fbe24be48f2d80f7204df286633611c6 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Mon, 22 Apr 2024 12:20:16 +0300 Subject: [PATCH 96/96] chore: update regex name --- packages/wallet/src/RegexUtil.ts | 2 +- packages/wallet/src/W3mFrameHelpers.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/wallet/src/RegexUtil.ts b/packages/wallet/src/RegexUtil.ts index c3cdbab3eb..0b046ae5f4 100644 --- a/packages/wallet/src/RegexUtil.ts +++ b/packages/wallet/src/RegexUtil.ts @@ -1,5 +1,5 @@ export const RegexUtil = { address: /^0x(?:[A-Fa-f0-9]{40})$/u, transactionHash: /^0x(?:[A-Fa-f0-9]{64})$/u, - transactionHashLong: /^0x(?:[A-Fa-f0-9]{130})$/u + signedMessage: /^0x(?:[A-Fa-f0-9]{130})$/u } diff --git a/packages/wallet/src/W3mFrameHelpers.ts b/packages/wallet/src/W3mFrameHelpers.ts index fabc1c8d87..40eeb71071 100644 --- a/packages/wallet/src/W3mFrameHelpers.ts +++ b/packages/wallet/src/W3mFrameHelpers.ts @@ -87,7 +87,7 @@ export const W3mFrameHelpers = { const isPayloadString = typeof payload === 'string' const isTransactionHash = isPayloadString && - (payload.match(RegexUtil.transactionHash) || payload.match(RegexUtil.transactionHashLong)) + (payload.match(RegexUtil.transactionHash) || payload.match(RegexUtil.signedMessage)) if (isTransactionHash) { return W3mFrameConstants.RPC_RESPONSE_TYPE_TX