diff --git a/package.json b/package.json index 11e98093d7..1fe9fda819 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "interbtc-ui", - "version": "2.41.15", + "version": "2.41.16", "private": true, "dependencies": { "@acala-network/sdk-core": "^4.1.5", @@ -81,6 +81,7 @@ "redux": "^4.0.5", "styled-components": "^5.3.5", "typescript": "4.3.2", + "viem": "^2.21.34", "web-vitals": "^1.0.1", "yup": "^0.32.11" }, diff --git a/src/App.tsx b/src/App.tsx index 84a8e033ab..02011c9fc1 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -21,6 +21,7 @@ import * as constants from './constants'; import { FeatureFlags, useFeatureFlag } from './hooks/use-feature-flag'; const BTC = React.lazy(() => import(/* webpackChunkName: 'btc' */ '@/pages/BTC')); +const BOB = React.lazy(() => import(/* webpackChunkName: 'bob' */ '@/pages/BOB')); const Strategies = React.lazy(() => import(/* webpackChunkName: 'strategies' */ '@/pages/Strategies')); const Strategy = React.lazy(() => import(/* webpackChunkName: 'strategy' */ '@/pages/Strategies/Strategy')); const SendAndReceive = React.lazy(() => import(/* webpackChunkName: 'sendAndReceive' */ '@/pages/SendAndReceive')); @@ -119,6 +120,9 @@ const App = (): JSX.Element => { + + + {isStrategiesEnabled && ( <> diff --git a/src/components/AppAlert/AppAlert.styles.tsx b/src/components/AppAlert/AppAlert.styles.tsx new file mode 100644 index 0000000000..1eec8563d2 --- /dev/null +++ b/src/components/AppAlert/AppAlert.styles.tsx @@ -0,0 +1,11 @@ +import styled from "styled-components"; + +import { CTA, theme } from "@/component-library"; + +const StyledCloseCTA = styled(CTA)` + position: absolute; + right: ${theme.spacing.spacing2}; + top: 0; +`; + +export { StyledCloseCTA }; diff --git a/src/components/AppAlert/AppAlert.tsx b/src/components/AppAlert/AppAlert.tsx new file mode 100644 index 0000000000..2cb44f9016 --- /dev/null +++ b/src/components/AppAlert/AppAlert.tsx @@ -0,0 +1,29 @@ +import { XMark } from "@/assets/icons"; +import { Alert, Flex, P } from "@/component-library"; +import { LocalStorageKey,useLocalStorage } from "@/hooks/use-local-storage"; + +import { StyledCloseCTA } from "./AppAlert.styles"; + +type Props = { + alertText: string +} + +const AppAlert = ({ alertText }: Props): JSX.Element => { + const [isAlertOpen, setIsAlertOpen] = useLocalStorage(LocalStorageKey.APP_ALERT_BANNER, true); + + return ( + <> + {isAlertOpen && ( + + +

{alertText}

+
+ setIsAlertOpen(false)}> + + +
+ )} + + );}; + +export { AppAlert }; diff --git a/src/components/AppAlert/index.tsx b/src/components/AppAlert/index.tsx new file mode 100644 index 0000000000..3d4f29c994 --- /dev/null +++ b/src/components/AppAlert/index.tsx @@ -0,0 +1 @@ +export { AppAlert } from './AppAlert'; diff --git a/src/components/Layout/Layout.tsx b/src/components/Layout/Layout.tsx index dc82b1cdb6..b3de158d8d 100644 --- a/src/components/Layout/Layout.tsx +++ b/src/components/Layout/Layout.tsx @@ -1,5 +1,8 @@ +import { APP_NAME } from '@/config/relay-chains'; + import Sidebar from '../../legacy-components/Sidebar'; import Topbar from '../../legacy-components/Topbar'; +import { AppAlert } from '../AppAlert'; import { StyledWrapper } from './Layout.styles'; interface Props { @@ -8,12 +11,15 @@ interface Props { } const Layout = ({ className, children }: Props): JSX.Element => ( - - - -
{children}
-
-
+ <> + + + + +
{children}
+
+
+ ); export { Layout }; diff --git a/src/hooks/use-local-storage.ts b/src/hooks/use-local-storage.ts index 512bff8496..36bd83acc0 100644 --- a/src/hooks/use-local-storage.ts +++ b/src/hooks/use-local-storage.ts @@ -2,12 +2,14 @@ import { useLocalStorage as useLibLocalStorage } from 'react-use'; enum LocalStorageKey { TC_SIGNATURES = 'TC_SIGNATURES', - WALLET_WELCOME_BANNER = 'WALLET_WELCOME_BANNER' + WALLET_WELCOME_BANNER = 'WALLET_WELCOME_BANNER', + APP_ALERT_BANNER = 'APP_ALERT_BANNER' } type LocalStorageValueTypes = { [LocalStorageKey.TC_SIGNATURES]: { [account: string]: { version: string; isSigned: boolean } | boolean }; [LocalStorageKey.WALLET_WELCOME_BANNER]: boolean; + [LocalStorageKey.APP_ALERT_BANNER]: boolean; }; type Options = diff --git a/src/lib/form/schemas/bob.ts b/src/lib/form/schemas/bob.ts new file mode 100644 index 0000000000..c197b6ffab --- /dev/null +++ b/src/lib/form/schemas/bob.ts @@ -0,0 +1,20 @@ +import i18n from 'i18next'; + +import yup, { AddressType } from '../yup.custom'; + +const BOB_RECIPIENT_FIELD = 'bob-destination'; + +type BobFormData = { + [BOB_RECIPIENT_FIELD]?: string; +}; + +const bobSchema = (): yup.ObjectSchema => + yup.object().shape({ + [BOB_RECIPIENT_FIELD]: yup + .string() + .required(i18n.t('forms.please_enter_your_field', { field: 'recipient' })) + .address(AddressType.ETHEREUM) + }); + +export { BOB_RECIPIENT_FIELD, bobSchema }; +export type { BobFormData }; diff --git a/src/lib/form/schemas/index.ts b/src/lib/form/schemas/index.ts index 06d05adcb3..7965a0b3ad 100644 --- a/src/lib/form/schemas/index.ts +++ b/src/lib/form/schemas/index.ts @@ -1,3 +1,4 @@ +export * from './bob'; export * from './btc'; export * from './loans'; export * from './pools'; diff --git a/src/lib/form/validate.ts b/src/lib/form/validate.ts index 5e1c82adeb..6a523a9aee 100644 --- a/src/lib/form/validate.ts +++ b/src/lib/form/validate.ts @@ -1,5 +1,6 @@ import { decodeAddress, encodeAddress } from '@polkadot/keyring'; import { hexToU8a, isHex } from '@polkadot/util'; +import { isAddress } from 'viem'; import { BTC_ADDRESS_REGEX } from '@/constants'; @@ -8,6 +9,8 @@ const btcAddressRegex = new RegExp(BTC_ADDRESS_REGEX); // TODO: use library instead const isValidBTCAddress = (address: string): boolean => btcAddressRegex.test(address); +const isValidEthereumAddress = (address: string): boolean => isAddress(address); + const isValidRelayAddress = (address: string): boolean => { try { encodeAddress(isHex(address) ? hexToU8a(address) : decodeAddress(address)); @@ -18,4 +21,4 @@ const isValidRelayAddress = (address: string): boolean => { } }; -export { isValidBTCAddress, isValidRelayAddress }; +export { isValidBTCAddress, isValidEthereumAddress, isValidRelayAddress }; diff --git a/src/lib/form/yup.custom.ts b/src/lib/form/yup.custom.ts index b24a1b9af0..649cfd1359 100644 --- a/src/lib/form/yup.custom.ts +++ b/src/lib/form/yup.custom.ts @@ -6,7 +6,7 @@ import i18n from 'i18next'; import * as yup from 'yup'; import { AnyObject, Maybe } from 'yup/lib/types'; -import { isValidBTCAddress, isValidRelayAddress } from './validate'; +import { isValidBTCAddress, isValidEthereumAddress, isValidRelayAddress } from './validate'; yup.addMethod(yup.string, 'requiredAmount', function (action: string, customMessage?: string) { return this.transform((value) => (isNaN(value) ? undefined : value)).test('requiredAmount', (value, ctx) => { @@ -108,12 +108,14 @@ yup.addMethod( enum AddressType { RELAY_CHAIN, - BTC + BTC, + ETHEREUM } const addressValidationMap = { [AddressType.RELAY_CHAIN]: isValidRelayAddress, - [AddressType.BTC]: isValidBTCAddress + [AddressType.BTC]: isValidBTCAddress, + [AddressType.ETHEREUM]: isValidEthereumAddress }; yup.addMethod( diff --git a/src/pages/BOB/BOB.styles.tsx b/src/pages/BOB/BOB.styles.tsx new file mode 100644 index 0000000000..33efc3bb0a --- /dev/null +++ b/src/pages/BOB/BOB.styles.tsx @@ -0,0 +1,11 @@ +import styled from 'styled-components'; + +import { Flex } from '@/component-library'; + +const StyledWrapper = styled(Flex)` + max-width: 540px; + width: 100%; + margin: 0 auto; +`; + +export { StyledWrapper }; diff --git a/src/pages/BOB/BOB.tsx b/src/pages/BOB/BOB.tsx new file mode 100644 index 0000000000..d36765af84 --- /dev/null +++ b/src/pages/BOB/BOB.tsx @@ -0,0 +1,202 @@ +import { mergeProps } from '@react-aria/utils'; +import { useEffect, useMemo } from 'react'; +import { useState } from 'react'; +import { withErrorBoundary } from 'react-error-boundary'; +import { useMutation } from 'react-query'; + +import { Card, Divider, Flex, H1, Input, P, TextLink } from '@/component-library'; +import { AuthCTA, MainContainer } from '@/components'; +import ErrorFallback from '@/legacy-components/ErrorFallback'; +import { useForm } from '@/lib/form'; +import { BOB_RECIPIENT_FIELD, BobFormData, bobSchema } from '@/lib/form/schemas'; +import { KeyringPair, useSubstrateSecureState } from '@/lib/substrate'; + +import { NotificationToastType, useNotifications } from '../../utils/context/Notifications'; +import { signMessage } from '../../utils/helpers/wallet'; +import { StyledWrapper } from './BOB.styles'; + +const postSignature = async (account: KeyringPair, ethereumAddress: string) => { + const legalText = await fetch(`https://bob-intr-drop.interlay.workers.dev/legaltext`); + + const message = await legalText.text(); + + const signerResult = await signMessage(account, message); + + if (!signerResult?.signature) { + throw new Error('Failed to sign message'); + } + + return fetch(`https://bob-intr-drop.interlay.workers.dev/accept`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + accountid: account.address, + signature: signerResult?.signature, + evmAddress: ethereumAddress + }) + }); +}; + +const checkAddress = async (address: string) => { + // This checks if a given address is eligible for claiming + const response = await fetch(`https://bob-intr-drop.interlay.workers.dev/check/${address}`); + + // If an address is not eligible, error code 499 is returned. We check this here + // to distinguish address not found from api errors/ + if (!response.ok && response.status !== 499) { + throw new Error('There was an error checking the address'); + } + + return response.ok; +}; + +const BOB = (): JSX.Element => { + const [isEligible, setIsEligible] = useState(false); + + const { selectedAccount } = useSubstrateSecureState(); + const notifications = useNotifications(); + + const submitEthereumAddressMutation = useMutation( + (variables: { selectedAccount: KeyringPair; ethereumAddress: string }) => + postSignature(variables.selectedAccount, variables.ethereumAddress), + { + onError: async (_, variables) => { + notifications.show(variables.selectedAccount.address, { + type: NotificationToastType.STANDARD, + props: { variant: 'error', title: 'Something went wrong. Please try again.' } + }); + }, + onSuccess: async (data, variables) => { + // 409 is a success response to indicate that an EVM address has already been + // submitted for the signing account so we show the user an error notification. + data.status === 409 + ? notifications.show(variables.selectedAccount.address, { + type: NotificationToastType.STANDARD, + props: { + variant: 'error', + title: 'Already submitted', + description: 'An EVM address has already been submitted for this account.' + } + }) + : notifications.show(variables.selectedAccount.address, { + type: NotificationToastType.STANDARD, + props: { variant: 'success', title: 'Address submitted' } + }); + + form.resetForm(); + } + } + ); + + const initialValues = useMemo( + () => ({ + [BOB_RECIPIENT_FIELD]: '' + }), + // eslint-disable-next-line react-hooks/exhaustive-deps + [] + ); + + const handleSubmit = async (values: BobFormData) => { + const ethereumAddress = values[BOB_RECIPIENT_FIELD]; + + if (!selectedAccount || !ethereumAddress) return; + + submitEthereumAddressMutation.mutate({ selectedAccount, ethereumAddress }); + }; + + const form = useForm({ + initialValues, + validationSchema: bobSchema(), + onSubmit: handleSubmit + }); + + useEffect(() => { + if (!selectedAccount?.address) return; + + const setEligibility = async () => { + const isAddressValid = await checkAddress(selectedAccount.address); + + setIsEligible(isAddressValid); + }; + + setEligibility(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [selectedAccount?.address]); + + // Reset mutation on account change + useEffect(() => { + if (submitEthereumAddressMutation.isLoading && selectedAccount?.address) { + submitEthereumAddressMutation.reset(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [selectedAccount?.address]); + + return ( + + + +

+ BOB x Interlay +

+ + + {isEligible ? ( + <> + +

+ Claim your{' '} + + BOB + {' '} + x Interlay exclusive NFT badge today. +

+

+ Only original Interlay community members are eligible. Simply submit the EVM address you'd like + to receive the NFT and sign the transaction with your Interlay account to prove your community + status. +

+

We'll let you know in Interlay Discord when the NFT will be available to claim.

+
+
+ + + + + + + Claim your NFT + + + +
+ + ) : // eslint-disable-next-line no-negated-condition + !selectedAccount ? ( + +

Please connect your wallet

+
+ ) : ( + +

Sorry, this account is not eligible.

+
+ )} +
+
+
+
+ ); +}; + +export default withErrorBoundary(BOB, { + FallbackComponent: ErrorFallback, + onReset: () => { + window.location.reload(); + } +}); diff --git a/src/pages/BOB/index.tsx b/src/pages/BOB/index.tsx new file mode 100644 index 0000000000..d6b5215ce4 --- /dev/null +++ b/src/pages/BOB/index.tsx @@ -0,0 +1,3 @@ +import BOB from './BOB'; + +export default BOB; diff --git a/src/utils/constants/links.ts b/src/utils/constants/links.ts index 89a66a3eec..288c7183f3 100644 --- a/src/utils/constants/links.ts +++ b/src/utils/constants/links.ts @@ -18,6 +18,7 @@ const URL_PARAMETERS = Object.freeze({ const PAGES = Object.freeze({ HOME: '/', BTC: '/btc', + BOB: '/bob-x-interlay', STRATEGIES: '/strategies', STRATEGY: `/strategies/:${URL_PARAMETERS.STRATEGY.TYPE}`, SEND_AND_RECEIVE: '/send-and-receive', diff --git a/yarn.lock b/yarn.lock index db31c24b69..d831ecf47c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -128,6 +128,11 @@ resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.4.0.tgz#728c484f4e10df03d5a3acd0d8adcbbebff8ad63" integrity sha512-Ff9+ksdQQB3rMncgqDK78uLznstjyfIf2Arnh22pW8kBpLs6rpKDwgnZT46hin5Hl1WzazzK64DOrhSwYpS7bQ== +"@adraffy/ens-normalize@^1.10.1": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.11.0.tgz#42cc67c5baa407ac25059fcd7d405cc5ecdb0c33" + integrity sha512-/3DDPKHqqIqxUULp8yP4zODUY1i+2xvVWsv8A79xGWdCAG+8sb0hRh0Rk2QyOJUnnbyPUAZYcpBuRe3nS2OIUg== + "@ampproject/remapping@^2.2.0": version "2.3.0" resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" @@ -2603,6 +2608,13 @@ call-me-maybe "^1.0.1" glob-to-regexp "^0.3.0" +"@noble/curves@1.8.1", "@noble/curves@^1.6.0", "@noble/curves@~1.8.1": + version "1.8.1" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.8.1.tgz#19bc3970e205c99e4bdb1c64a4785706bce497ff" + integrity sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ== + dependencies: + "@noble/hashes" "1.7.1" + "@noble/curves@^1.3.0": version "1.4.2" resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.4.2.tgz#40309198c76ed71bc6dbf7ba24e81ceb4d0d1fe9" @@ -2625,6 +2637,11 @@ resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.4.0.tgz#45814aa329f30e4fe0ba49426f49dfccdd066426" integrity sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg== +"@noble/hashes@1.7.1", "@noble/hashes@^1.5.0", "@noble/hashes@~1.7.1": + version "1.7.1" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.7.1.tgz#5738f6d765710921e7a751e00c20ae091ed8db0f" + integrity sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ== + "@noble/secp256k1@1.5.5": version "1.5.5" resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.5.5.tgz#315ab5745509d1a8c8e90d0bdf59823ccf9bcfc3" @@ -6166,6 +6183,28 @@ resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.9.tgz#e5e142fbbfe251091f9c5f1dd4c834ac04c3dbd1" integrity sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg== +"@scure/base@~1.2.2", "@scure/base@~1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.2.4.tgz#002eb571a35d69bdb4c214d0995dff76a8dcd2a9" + integrity sha512-5Yy9czTO47mqz+/J8GM6GIId4umdCk1wc1q8rKERQulIoc8VP9pzDcghv10Tl2E7R96ZUx/PhND3ESYUQX8NuQ== + +"@scure/bip32@1.6.2", "@scure/bip32@^1.5.0": + version "1.6.2" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.6.2.tgz#093caa94961619927659ed0e711a6e4bf35bffd0" + integrity sha512-t96EPDMbtGgtb7onKKqxRLfE5g05k7uHnHRM2xdE6BP/ZmxaLtPek4J4KfVn/90IQNrU1IOAqMgiDtUdtbe3nw== + dependencies: + "@noble/curves" "~1.8.1" + "@noble/hashes" "~1.7.1" + "@scure/base" "~1.2.2" + +"@scure/bip39@1.5.4", "@scure/bip39@^1.4.0": + version "1.5.4" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.5.4.tgz#07fd920423aa671be4540d59bdd344cc1461db51" + integrity sha512-TFM4ni0vKvCfBpohoh+/lY05i9gRbSwXWngAsF4CABQxoaOHijxuaZ2R6cStDQ5CHtHO9aGJTr4ksVJASRRyMA== + dependencies: + "@noble/hashes" "~1.7.1" + "@scure/base" "~1.2.4" + "@sinclair/typebox@^0.27.8": version "0.27.8" resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" @@ -7331,7 +7370,7 @@ dependencies: mini-svg-data-uri "^1.2.3" -"@tailwindcss/postcss7-compat@^2.0.3": +"@tailwindcss/postcss7-compat@^2.0.3", "tailwindcss@npm:@tailwindcss/postcss7-compat": version "2.2.17" resolved "https://registry.yarnpkg.com/@tailwindcss/postcss7-compat/-/postcss7-compat-2.2.17.tgz#dc78f3880a2af84163150ff426a39e42b9ae8922" integrity sha512-3h2svqQAqYHxRZ1KjsJjZOVTQ04m29LjfrLjXyZZEJuvUuJN+BCIF9GI8vhE1s0plS0mogd6E6YLg6mu4Wv/Vw== @@ -8407,6 +8446,11 @@ abab@^2.0.3, abab@^2.0.5: resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== +abitype@1.0.8, abitype@^1.0.6: + version "1.0.8" + resolved "https://registry.yarnpkg.com/abitype/-/abitype-1.0.8.tgz#3554f28b2e9d6e9f35eb59878193eabd1b9f46ba" + integrity sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg== + accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: version "1.3.8" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" @@ -12603,16 +12647,16 @@ event-emitter@^0.3.5: d "1" es5-ext "~0.10.14" +eventemitter3@5.0.1, eventemitter3@^5.0.0, eventemitter3@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" + integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== + eventemitter3@^4.0.0, eventemitter3@^4.0.7: version "4.0.7" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== -eventemitter3@^5.0.0, eventemitter3@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" - integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== - events@^3.0.0, events@^3.2.0: version "3.3.0" resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" @@ -15120,6 +15164,11 @@ isomorphic-unfetch@^3.1.0: node-fetch "^2.6.1" unfetch "^4.2.0" +isows@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/isows/-/isows-1.0.6.tgz#0da29d706fa51551c663c627ace42769850f86e7" + integrity sha512-lPHCayd40oW98/I0uvgaHKWCSvkzY27LjWLbtzOm64yQ+G3Q5npjjbdppU65iZXkK1Zt+kH9pfegli0AYfwYYw== + issue-parser@6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/issue-parser/-/issue-parser-6.0.0.tgz#b1edd06315d4f2044a9755daf85fdafde9b4014a" @@ -17477,6 +17526,19 @@ os-tmpdir@~1.0.2: resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== +ox@0.6.7: + version "0.6.7" + resolved "https://registry.yarnpkg.com/ox/-/ox-0.6.7.tgz#afd53f2ecef68b8526660e9d29dee6e6b599a832" + integrity sha512-17Gk/eFsFRAZ80p5eKqv89a57uXjd3NgIf1CaXojATPBuujVc/fQSVhBeAU9JCRB+k7J50WQAyWTxK19T9GgbA== + dependencies: + "@adraffy/ens-normalize" "^1.10.1" + "@noble/curves" "^1.6.0" + "@noble/hashes" "^1.5.0" + "@scure/bip32" "^1.5.0" + "@scure/bip39" "^1.4.0" + abitype "^1.0.6" + eventemitter3 "5.0.1" + p-all@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-all/-/p-all-2.1.0.tgz#91419be56b7dee8fe4c5db875d55e0da084244a0" @@ -21508,47 +21570,6 @@ tailwindcss-pseudo-elements@^1.5.1: resolved "https://registry.yarnpkg.com/tailwindcss-pseudo-elements/-/tailwindcss-pseudo-elements-1.5.1.tgz#81603ddd96670d0d18bc85dc26c742b2cfe2aa8a" integrity sha512-B7ABn5MeXWH1aXYSXui+MenSjv8bCd2PVIJEXue/sVKGuPYo3FKfpKzkcNXUUe/wgFTLbpVArlU0rCD6rZYkPg== -"tailwindcss@npm:@tailwindcss/postcss7-compat": - version "2.2.17" - resolved "https://registry.yarnpkg.com/@tailwindcss/postcss7-compat/-/postcss7-compat-2.2.17.tgz#dc78f3880a2af84163150ff426a39e42b9ae8922" - integrity sha512-3h2svqQAqYHxRZ1KjsJjZOVTQ04m29LjfrLjXyZZEJuvUuJN+BCIF9GI8vhE1s0plS0mogd6E6YLg6mu4Wv/Vw== - dependencies: - arg "^5.0.1" - autoprefixer "^9" - bytes "^3.0.0" - chalk "^4.1.2" - chokidar "^3.5.2" - color "^4.0.1" - cosmiconfig "^7.0.1" - detective "^5.2.0" - didyoumean "^1.2.2" - dlv "^1.1.3" - fast-glob "^3.2.7" - fs-extra "^10.0.0" - glob-parent "^6.0.1" - html-tags "^3.1.0" - is-color-stop "^1.1.0" - is-glob "^4.0.1" - lodash "^4.17.21" - lodash.topath "^4.5.2" - modern-normalize "^1.1.0" - node-emoji "^1.11.0" - normalize-path "^3.0.0" - object-hash "^2.2.0" - postcss "^7" - postcss-functions "^3" - postcss-js "^2" - postcss-load-config "^3.1.0" - postcss-nested "^4" - postcss-selector-parser "^6.0.6" - postcss-value-parser "^4.1.0" - pretty-hrtime "^1.0.3" - purgecss "^4.0.3" - quick-lru "^5.1.1" - reduce-css-calc "^2.1.8" - resolve "^1.20.0" - tmp "^0.2.1" - tapable@^1.0.0, tapable@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" @@ -22618,6 +22639,20 @@ vfile@^4.0.0: unist-util-stringify-position "^2.0.0" vfile-message "^2.0.0" +viem@^2.21.34: + version "2.23.5" + resolved "https://registry.yarnpkg.com/viem/-/viem-2.23.5.tgz#50fb9ea0701d58e6a7a1714ecaa5edfa100bb391" + integrity sha512-cUfBHdFQHmBlPW0loFXda0uZcoU+uJw3NRYQRwYgkrpH6PgovH8iuVqDn6t1jZk82zny4wQL54c9dCX2W9kLMg== + dependencies: + "@noble/curves" "1.8.1" + "@noble/hashes" "1.7.1" + "@scure/bip32" "1.6.2" + "@scure/bip39" "1.5.4" + abitype "1.0.8" + isows "1.0.6" + ox "0.6.7" + ws "8.18.0" + vm-browserify@^1.0.1: version "1.1.2" resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" @@ -23344,6 +23379,11 @@ ws@7.4.6: resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== +ws@8.18.0, ws@^8.15.1, ws@^8.16.0, ws@^8.2.3, ws@^8.8.1: + version "8.18.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" + integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== + ws@^6.2.1: version "6.2.3" resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.3.tgz#ccc96e4add5fd6fedbc491903075c85c5a11d9ee" @@ -23356,11 +23396,6 @@ ws@^7.3.1, ws@^7.4.6: resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9" integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ== -ws@^8.15.1, ws@^8.16.0, ws@^8.2.3, ws@^8.8.1: - version "8.18.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" - integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== - x-default-browser@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/x-default-browser/-/x-default-browser-0.4.0.tgz#70cf0da85da7c0ab5cb0f15a897f2322a6bdd481"