diff --git a/.changeset/old-beers-agree.md b/.changeset/old-beers-agree.md new file mode 100644 index 00000000..4c944e90 --- /dev/null +++ b/.changeset/old-beers-agree.md @@ -0,0 +1,18 @@ +--- +'@reown/appkit-coinbase-ethers-react-native': minor +'@reown/appkit-coinbase-wagmi-react-native': minor +'@reown/appkit-scaffold-utils-react-native': minor +'@reown/appkit-auth-ethers-react-native': minor +'@reown/appkit-auth-wagmi-react-native': minor +'@reown/appkit-scaffold-react-native': minor +'@reown/appkit-ethers5-react-native': minor +'@reown/appkit-common-react-native': minor +'@reown/appkit-ethers-react-native': minor +'@reown/appkit-wallet-react-native': minor +'@reown/appkit-wagmi-react-native': minor +'@reown/appkit-core-react-native': minor +'@reown/appkit-siwe-react-native': minor +'@reown/appkit-ui-react-native': minor +--- + +feat: added wallet features for universal wallets diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml index 110c2f25..b8d1ab43 100644 --- a/.github/workflows/snapshot.yml +++ b/.github/workflows/snapshot.yml @@ -20,7 +20,7 @@ jobs: uses: ./.github/actions/setup - name: Publish Snapshots - continue-on-error: true + continue-on-error: false env: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/apps/gallery/stories/composites/AccountButton.stories.tsx b/apps/gallery/stories/composites/AccountButton.stories.tsx index 807c542f..d9d45f86 100644 --- a/apps/gallery/stories/composites/AccountButton.stories.tsx +++ b/apps/gallery/stories/composites/AccountButton.stories.tsx @@ -12,8 +12,8 @@ const meta: Meta = { address: { control: { type: 'text' } }, - isProfileName: { - control: { type: 'boolean' } + profileName: { + control: { type: 'text' } }, networkSrc: { control: { type: 'text' } @@ -26,8 +26,7 @@ const meta: Meta = { avatarSrc: avatarImageSrc, address: '0xDBbD65026a07cFbFa1aa92744E4D69951686077d', networkSrc: networkImageSrc, - balance: '0.527 ETH', - isProfileName: false + balance: '0.527 ETH' } }; @@ -41,7 +40,7 @@ export const Default: Story = { address={args.address} networkSrc={args.networkSrc} balance={args.balance} - isProfileName={args.isProfileName} + profileName={args.profileName} /> ) }; diff --git a/apps/gallery/stories/composites/Chip.stories.tsx b/apps/gallery/stories/composites/Chip.stories.tsx index cd84fbc5..fcef6946 100644 --- a/apps/gallery/stories/composites/Chip.stories.tsx +++ b/apps/gallery/stories/composites/Chip.stories.tsx @@ -1,13 +1,7 @@ import type { Meta, StoryObj } from '@storybook/react'; import { Chip } from '@reown/appkit-ui-react-native'; -import { - chipOptions, - externalLabel, - externalLink, - iconOptions, - walletImageSrc -} from '../../utils/PresetUtils'; +import { chipOptions, externalLabel, iconOptions, walletImageSrc } from '../../utils/PresetUtils'; const meta: Meta = { component: Chip, @@ -23,9 +17,6 @@ const meta: Meta = { disabled: { control: { type: 'boolean' } }, - link: { - control: { type: 'text' } - }, label: { control: { type: 'text' } }, @@ -42,7 +33,6 @@ const meta: Meta = { size: 'md', disabled: false, icon: 'disconnect', - link: externalLink, label: externalLabel, imageSrc: walletImageSrc } @@ -57,7 +47,7 @@ export const Default: Story = { variant={args.variant} size={args.size} disabled={args.disabled} - link={args.link} + onPress={() => {}} label={args.label} imageSrc={args.imageSrc} icon={args.icon} diff --git a/apps/gallery/stories/composites/ListItem.stories.tsx b/apps/gallery/stories/composites/ListItem.stories.tsx index 70c0883f..f6fa9a9b 100644 --- a/apps/gallery/stories/composites/ListItem.stories.tsx +++ b/apps/gallery/stories/composites/ListItem.stories.tsx @@ -10,18 +10,10 @@ const meta: Meta = { imageSrc: { control: { type: 'text' } }, - variant: { - options: ['image', 'icon'], - control: { type: 'select' } - }, icon: { options: iconOptions, control: { type: 'select' } }, - iconVariant: { - options: ['blue', 'overlay'], - control: { type: 'select' } - }, disabled: { control: { type: 'boolean' } }, @@ -33,10 +25,8 @@ const meta: Meta = { } }, args: { - variant: 'image', imageSrc: networkImageSrc, icon: 'swapHorizontal', - iconVariant: 'blue', disabled: false, chevron: true, loading: false @@ -50,10 +40,8 @@ export const Default: Story = { render: (args: any) => ( = { component: NetworkButton, argTypes: { - variant: { - options: ['fill', 'shade'], - control: { type: 'select' } - }, disabled: { control: { type: 'boolean' } }, @@ -21,7 +17,6 @@ const meta: Meta = { } }, args: { - variant: 'fill', disabled: false, children: 'Ethereum', imageSrc: networkImageSrc @@ -33,12 +28,7 @@ type Story = StoryObj; export const Default: Story = { render: args => ( - {}} - > + {}}> {args.children} ) diff --git a/apps/gallery/utils/PresetUtils.ts b/apps/gallery/utils/PresetUtils.ts index 10da20dc..8e450efd 100644 --- a/apps/gallery/utils/PresetUtils.ts +++ b/apps/gallery/utils/PresetUtils.ts @@ -32,8 +32,6 @@ export const avatarImageSrc = export const wcUri = 'wc:139520827546986d057472f8bbd7ef0484409458034b61cca59d908563773c7a@2?relay-protocol=irn&symKey=43b5fad11bf07bc8a0aa12231435a4ad3e72e2d1fa257cf191a90ec5b62cb0a'; -export const externalLink = 'https://www.fireblocks.com'; - export const externalLabel = 'www.fireblocks.com'; export const colorOptions: ColorType[] = [ diff --git a/packages/common/package.json b/packages/common/package.json index 8475dbe4..06c72cd5 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -11,6 +11,10 @@ "test": "jest --passWithNoTests", "lint": "eslint . --ext .js,.jsx,.ts,.tsx" }, + "dependencies": { + "bignumber.js": "9.1.2", + "dayjs": "1.11.10" + }, "files": [ "src", "lib" diff --git a/packages/common/src/contracts/erc20.ts b/packages/common/src/contracts/erc20.ts new file mode 100644 index 00000000..cf48b5c1 --- /dev/null +++ b/packages/common/src/contracts/erc20.ts @@ -0,0 +1,48 @@ +export const erc20ABI = [ + { + type: 'function', + name: 'transfer', + stateMutability: 'nonpayable', + inputs: [ + { + name: '_to', + type: 'address' + }, + { + name: '_value', + type: 'uint256' + } + ], + outputs: [ + { + name: '', + type: 'bool' + } + ] + }, + { + type: 'function', + name: 'transferFrom', + stateMutability: 'nonpayable', + inputs: [ + { + name: '_from', + type: 'address' + }, + { + name: '_to', + type: 'address' + }, + { + name: '_value', + type: 'uint256' + } + ], + outputs: [ + { + name: '', + type: 'bool' + } + ] + } +]; diff --git a/packages/common/src/contracts/usdt.ts b/packages/common/src/contracts/usdt.ts new file mode 100644 index 00000000..8d2dbe95 --- /dev/null +++ b/packages/common/src/contracts/usdt.ts @@ -0,0 +1,43 @@ +export const usdtABI = [ + { + type: 'function', + name: 'transfer', + stateMutability: 'nonpayable', + inputs: [ + { + name: 'recipient', + type: 'address' + }, + { + name: 'amount', + type: 'uint256' + } + ], + outputs: [] + }, + { + type: 'function', + name: 'transferFrom', + stateMutability: 'nonpayable', + inputs: [ + { + name: 'sender', + type: 'address' + }, + { + name: 'recipient', + type: 'address' + }, + { + name: 'amount', + type: 'uint256' + } + ], + outputs: [ + { + name: '', + type: 'bool' + } + ] + } +]; diff --git a/packages/common/src/index.ts b/packages/common/src/index.ts index f27beb61..13f0e979 100644 --- a/packages/common/src/index.ts +++ b/packages/common/src/index.ts @@ -1,2 +1,8 @@ export { ConstantsUtil } from './utils/ConstantsUtil'; +export { ContractUtil } from './utils/ContractUtil'; +export { DateUtil } from './utils/DateUtil'; +export { NamesUtil } from './utils/NamesUtil'; export { NetworkUtil } from './utils/NetworkUtil'; +export { NumberUtil } from './utils/NumberUtil'; +export { erc20ABI } from './contracts/erc20'; +export * from './utils/TypeUtil'; diff --git a/packages/common/src/utils/ConstantsUtil.ts b/packages/common/src/utils/ConstantsUtil.ts index 064d5444..4d21f178 100644 --- a/packages/common/src/utils/ConstantsUtil.ts +++ b/packages/common/src/utils/ConstantsUtil.ts @@ -1,7 +1,25 @@ export const ConstantsUtil = { + WC_NAME_SUFFIX: '.reown.id', + WC_NAME_SUFFIX_LEGACY: '.wcn.id', BLOCKCHAIN_API_RPC_URL: 'https://rpc.walletconnect.org', PULSE_API_URL: 'https://pulse.walletconnect.org', API_URL: 'https://api.web3modal.org', COINBASE_CONNECTOR_ID: 'coinbaseWallet', - COINBASE_EXPLORER_ID: 'fd20dc426fb37566d803205b19bbc1d4096b248ac04548e3cfb6b3a38bd033aa' + COINBASE_EXPLORER_ID: 'fd20dc426fb37566d803205b19bbc1d4096b248ac04548e3cfb6b3a38bd033aa', + USDT_CONTRACT_ADDRESSES: [ + // Mainnet + '0xdac17f958d2ee523a2206206994597c13d831ec7', + // Polygon + '0xc2132d05d31c914a87c6611c10748aeb04b58e8f', + // Avalanche + '0x9702230a8ea53601f5cd2dc00fdbc13d4df4a8c7', + // Cosmos + '0x919C1c267BC06a7039e03fcc2eF738525769109c', + // Celo + '0x48065fbBE25f71C9282ddf5e1cD6D6A887483D5e', + // Binance + '0x55d398326f99059fF775485246999027B3197955', + // Arbitrum + '0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9' + ] }; diff --git a/packages/common/src/utils/ContractUtil.ts b/packages/common/src/utils/ContractUtil.ts new file mode 100644 index 00000000..d102267e --- /dev/null +++ b/packages/common/src/utils/ContractUtil.ts @@ -0,0 +1,13 @@ +import { erc20ABI } from '../contracts/erc20'; +import { usdtABI } from '../contracts/usdt'; +import { ConstantsUtil } from './ConstantsUtil'; + +export const ContractUtil = { + getERC20Abi: (tokenAddress: string) => { + if (ConstantsUtil.USDT_CONTRACT_ADDRESSES.includes(tokenAddress)) { + return usdtABI; + } + + return erc20ABI; + } +}; diff --git a/packages/common/src/utils/DateUtil.ts b/packages/common/src/utils/DateUtil.ts new file mode 100644 index 00000000..e6c09dcd --- /dev/null +++ b/packages/common/src/utils/DateUtil.ts @@ -0,0 +1,47 @@ +import dayjs from 'dayjs'; +import englishLocale from 'dayjs/locale/en.js'; +import relativeTime from 'dayjs/plugin/relativeTime.js'; +import updateLocale from 'dayjs/plugin/updateLocale.js'; + +dayjs.extend(relativeTime); +dayjs.extend(updateLocale); + +const localeObject = { + ...englishLocale, + name: 'en-web3-modal', + relativeTime: { + future: 'in %s', + past: '%s ago', + s: '%d sec', + m: '1 min', + mm: '%d min', + h: '1 hr', + hh: '%d hrs', + d: '1 d', + dd: '%d d', + M: '1 mo', + MM: '%d mo', + y: '1 yr', + yy: '%d yr' + } +}; + +dayjs.locale('en-appkit', localeObject); + +export const DateUtil = { + getYear(date: string = new Date().toISOString()) { + return dayjs(date).year(); + }, + + getRelativeDateFromNow(date: string | number) { + return dayjs(date).locale('en-appkit').fromNow(true); + }, + + formatDate(date: string | number, format = 'DD MMM') { + return dayjs(date).format(format); + }, + + getMonth(month: number) { + return dayjs().month(month).format('MMMM'); + } +}; diff --git a/packages/common/src/utils/NamesUtil.ts b/packages/common/src/utils/NamesUtil.ts new file mode 100644 index 00000000..312eda74 --- /dev/null +++ b/packages/common/src/utils/NamesUtil.ts @@ -0,0 +1,10 @@ +import { ConstantsUtil } from './ConstantsUtil'; + +export const NamesUtil = { + isReownName(value: string) { + return ( + value?.endsWith(ConstantsUtil.WC_NAME_SUFFIX_LEGACY) || + value?.endsWith(ConstantsUtil.WC_NAME_SUFFIX) + ); + } +}; diff --git a/packages/common/src/utils/NumberUtil.ts b/packages/common/src/utils/NumberUtil.ts new file mode 100644 index 00000000..760071ba --- /dev/null +++ b/packages/common/src/utils/NumberUtil.ts @@ -0,0 +1,31 @@ +import * as BigNumber from 'bignumber.js'; + +export const NumberUtil = { + bigNumber(value: BigNumber.BigNumber.Value) { + return new BigNumber.BigNumber(value); + }, + + /** + * Multiply two numbers represented as strings with BigNumber to handle decimals correctly + * @param a string + * @param b string + * @returns + */ + multiply(a: BigNumber.BigNumber.Value | undefined, b: BigNumber.BigNumber.Value | undefined) { + if (a === undefined || b === undefined) { + return BigNumber.BigNumber(0); + } + + const aBigNumber = new BigNumber.BigNumber(a); + const bBigNumber = new BigNumber.BigNumber(b); + + return aBigNumber.multipliedBy(bBigNumber); + }, + + roundNumber(number: number, threshold: number, fixed: number) { + const roundedNumber = + number.toString().length >= threshold ? Number(number).toFixed(fixed) : number; + + return roundedNumber; + } +}; diff --git a/packages/common/src/utils/TypeUtil.ts b/packages/common/src/utils/TypeUtil.ts new file mode 100644 index 00000000..b5f83c35 --- /dev/null +++ b/packages/common/src/utils/TypeUtil.ts @@ -0,0 +1,87 @@ +export interface Balance { + name: string; + symbol: string; + chainId: string; + address?: string; + value?: number; + price: number; + quantity: BalanceQuantity; + iconUrl: string; +} + +type BalanceQuantity = { + decimals: string; + numeric: string; +}; + +export type TransactionStatus = 'confirmed' | 'failed' | 'pending'; +export type TransactionDirection = 'in' | 'out' | 'self'; +export type TransactionImage = { + type: 'FUNGIBLE' | 'NFT' | undefined; + url: string | undefined; +}; + +export interface Transaction { + id: string; + metadata: TransactionMetadata; + transfers: TransactionTransfer[]; +} + +export interface TransactionMetadata { + application: { + iconUrl: string | null; + name: string | null; + }; + operationType: string; + hash: string; + minedAt: string; + sentFrom: string; + sentTo: string; + status: TransactionStatus; + nonce: number; + chain?: string; +} + +export interface TransactionTransfer { + fungible_info?: { + name?: string; + symbol?: string; + icon?: { + url: string; + }; + }; + nft_info?: TransactionNftInfo; + direction: TransactionDirection; + quantity: TransactionQuantity; + value?: number; + price?: number; +} + +export interface TransactionNftInfo { + name?: string; + content?: TransactionContent; + flags: TransactionNftInfoFlags; +} + +export interface TransactionNftInfoFlags { + is_spam: boolean; +} + +export interface TransactionContent { + preview?: TransactionPreview; + detail?: TransactionDetail; +} + +export interface TransactionPreview { + url: string; + content_type?: null; +} + +export interface TransactionDetail { + url: string; + content_type?: null; +} + +export interface TransactionQuantity { + numeric: string; +} diff --git a/packages/core/src/__tests__/controllers/AccountController.test.ts b/packages/core/src/__tests__/controllers/AccountController.test.ts index c309f475..2398d562 100644 --- a/packages/core/src/__tests__/controllers/AccountController.test.ts +++ b/packages/core/src/__tests__/controllers/AccountController.test.ts @@ -7,10 +7,15 @@ const balanceSymbol = 'ETH'; const profileName = 'john.eth'; const profileImage = 'https://ipfs.com/0x123.png'; +const initialState = { + isConnected: false, + tokenBalance: [] +}; + // -- Tests -------------------------------------------------------------------- describe('AccountController', () => { it('should have valid default state', () => { - expect(AccountController.state).toEqual({ isConnected: false }); + expect(AccountController.state).toEqual(initialState); }); it('should update state correctly on setIsConnected()', () => { @@ -42,6 +47,6 @@ describe('AccountController', () => { it('should update state correctly on resetAccount()', () => { AccountController.resetAccount(); - expect(AccountController.state).toEqual({ isConnected: false }); + expect(AccountController.state).toEqual(initialState); }); }); diff --git a/packages/core/src/__tests__/controllers/RouterController.test.ts b/packages/core/src/__tests__/controllers/RouterController.test.ts index c6a1c09d..8e5d56f1 100644 --- a/packages/core/src/__tests__/controllers/RouterController.test.ts +++ b/packages/core/src/__tests__/controllers/RouterController.test.ts @@ -5,7 +5,8 @@ describe('RouterController', () => { it('should have valid default state', () => { expect(RouterController.state).toEqual({ view: 'Connect', - history: ['Connect'] + history: ['Connect'], + transactionStack: [] }); }); @@ -13,7 +14,8 @@ describe('RouterController', () => { RouterController.push('Account'); expect(RouterController.state).toEqual({ view: 'Account', - history: ['Connect', 'Account'] + history: ['Connect', 'Account'], + transactionStack: [] }); }); @@ -21,7 +23,8 @@ describe('RouterController', () => { RouterController.push('Account'); expect(RouterController.state).toEqual({ view: 'Account', - history: ['Connect', 'Account'] + history: ['Connect', 'Account'], + transactionStack: [] }); }); @@ -29,7 +32,8 @@ describe('RouterController', () => { RouterController.goBack(); expect(RouterController.state).toEqual({ view: 'Connect', - history: ['Connect'] + history: ['Connect'], + transactionStack: [] }); }); @@ -37,7 +41,8 @@ describe('RouterController', () => { RouterController.goBack(); expect(RouterController.state).toEqual({ view: 'Connect', - history: ['Connect'] + history: ['Connect'], + transactionStack: [] }); }); @@ -45,7 +50,8 @@ describe('RouterController', () => { RouterController.reset('Account'); expect(RouterController.state).toEqual({ view: 'Account', - history: ['Account'] + history: ['Account'], + transactionStack: [] }); }); @@ -54,7 +60,8 @@ describe('RouterController', () => { RouterController.replace('Networks'); expect(RouterController.state).toEqual({ view: 'Networks', - history: ['Account', 'Networks'] + history: ['Account', 'Networks'], + transactionStack: [] }); }); @@ -67,7 +74,8 @@ describe('RouterController', () => { history: ['Account', 'Networks', 'ConnectingWalletConnect'], data: { wallet: { id: 'test', name: 'TestWallet' } - } + }, + transactionStack: [] }); }); }); diff --git a/packages/core/src/__tests__/controllers/SendController.test.ts b/packages/core/src/__tests__/controllers/SendController.test.ts new file mode 100644 index 00000000..4048144a --- /dev/null +++ b/packages/core/src/__tests__/controllers/SendController.test.ts @@ -0,0 +1,57 @@ +import { SendController } from '../../index'; + +// -- Setup -------------------------------------------------------------------- +const token = { + name: 'Optimism', + address: 'eip155:10:0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee', + symbol: 'OP', + chainId: 'eip155:10', + value: 6.05441523113072, + price: 4.5340112, + quantity: { + decimals: '18', + numeric: '1.335333100000000000' + }, + iconUrl: 'https://token-icons.s3.amazonaws.com/0x4200000000000000000000000000000000000042.png' +}; +const sendTokenAmount = 0.1; +const receiverAddress = '0xd8da6bf26964af9d7eed9e03e53415d37aa96045'; +const receiverProfileName = 'john.eth'; +const receiverProfileImageUrl = 'https://ipfs.com/0x123.png'; + +// -- Tests -------------------------------------------------------------------- +describe('SendController', () => { + it('should have valid default state', () => { + expect(SendController.state).toEqual({ loading: false }); + }); + + it('should update state correctly on setToken()', () => { + SendController.setToken(token); + expect(SendController.state.token).toEqual(token); + }); + + it('should update state correctly on setTokenAmount()', () => { + SendController.setTokenAmount(sendTokenAmount); + expect(SendController.state.sendTokenAmount).toEqual(sendTokenAmount); + }); + + it('should update state correctly on receiverAddress()', () => { + SendController.setReceiverAddress(receiverAddress); + expect(SendController.state.receiverAddress).toEqual(receiverAddress); + }); + + it('should update state correctly on receiverProfileName()', () => { + SendController.setReceiverProfileName(receiverProfileName); + expect(SendController.state.receiverProfileName).toEqual(receiverProfileName); + }); + + it('should update state correctly on setReceiverProfileImageUrl()', () => { + SendController.setReceiverProfileImageUrl(receiverProfileImageUrl); + expect(SendController.state.receiverProfileImageUrl).toEqual(receiverProfileImageUrl); + }); + + it('should update state correctly on resetSend()', () => { + SendController.resetSend(); + expect(SendController.state).toEqual({ loading: false }); + }); +}); diff --git a/packages/core/src/controllers/AccountController.ts b/packages/core/src/controllers/AccountController.ts index 116d28f2..344aafb8 100644 --- a/packages/core/src/controllers/AccountController.ts +++ b/packages/core/src/controllers/AccountController.ts @@ -1,8 +1,12 @@ import { proxy } from 'valtio'; import { subscribeKey as subKey } from 'valtio/utils'; +import type { Balance } from '@reown/appkit-common-react-native'; import { CoreHelperUtil } from '../utils/CoreHelperUtil'; import type { CaipAddress, ConnectedWalletInfo } from '../utils/TypeUtil'; +import { NetworkController } from './NetworkController'; +import { BlockchainApiController } from './BlockchainApiController'; +import { SnackController } from './SnackController'; // -- Types --------------------------------------------- // export interface AccountControllerState { @@ -11,6 +15,7 @@ export interface AccountControllerState { address?: string; balance?: string; balanceSymbol?: string; + tokenBalance?: Balance[]; profileName?: string; profileImage?: string; addressExplorerUrl?: string; @@ -21,7 +26,8 @@ type StateKey = keyof AccountControllerState; // -- State --------------------------------------------- // const state = proxy({ - isConnected: false + isConnected: false, + tokenBalance: [] }); // -- Controller ---------------------------------------- // @@ -50,6 +56,10 @@ export const AccountController = { state.balanceSymbol = balanceSymbol; }, + setTokenBalance(tokenBalance: AccountControllerState['tokenBalance']) { + state.tokenBalance = tokenBalance; + }, + setProfileName(profileName: AccountControllerState['profileName']) { state.profileName = profileName; }, @@ -66,6 +76,29 @@ export const AccountController = { state.addressExplorerUrl = explorerUrl; }, + async fetchTokenBalance() { + const chainId = NetworkController.state.caipNetwork?.id; + const address = AccountController.state.address; + + try { + if (address && chainId) { + const response = await BlockchainApiController.getBalance(address, chainId); + + if (!response) { + throw new Error('Failed to fetch token balance'); + } + + const filteredBalances = response.balances.filter( + balance => balance.quantity.decimals !== '0' + ); + + this.setTokenBalance(filteredBalances); + } + } catch (error) { + SnackController.showError('Failed to fetch token balance'); + } + }, + resetAccount() { state.isConnected = false; state.caipAddress = undefined; @@ -75,6 +108,7 @@ export const AccountController = { state.profileName = undefined; state.profileImage = undefined; state.addressExplorerUrl = undefined; + state.tokenBalance = []; state.connectedWalletInfo = undefined; } }; diff --git a/packages/core/src/controllers/ApiController.ts b/packages/core/src/controllers/ApiController.ts index de358ce2..dec37f8d 100644 --- a/packages/core/src/controllers/ApiController.ts +++ b/packages/core/src/controllers/ApiController.ts @@ -16,6 +16,7 @@ import { NetworkController } from './NetworkController'; import { OptionsController } from './OptionsController'; import { ConnectorController } from './ConnectorController'; import { ConnectionController } from './ConnectionController'; +import { ApiUtil } from '../utils/ApiUtil'; // -- Helpers ------------------------------------------- // const baseUrl = CoreHelperUtil.getApiUrl(); @@ -62,18 +63,13 @@ export const ApiController = { _getApiHeaders() { const { projectId, sdkType, sdkVersion } = OptionsController.state; - const reactNativeVersion = [ - Platform.constants.reactNativeVersion.major, - Platform.constants.reactNativeVersion.minor, - Platform.constants.reactNativeVersion.patch - ].join('.'); return { 'x-project-id': projectId, 'x-sdk-type': sdkType, 'x-sdk-version': sdkVersion, - 'User-Agent': `${Platform.OS}-${Platform.Version}@rn-${reactNativeVersion}`, - 'Origin': CoreHelperUtil.getBundleId() + 'User-Agent': ApiUtil.getUserAgent(), + 'Origin': ApiUtil.getOrigin() }; }, diff --git a/packages/core/src/controllers/BlockchainApiController.ts b/packages/core/src/controllers/BlockchainApiController.ts index 3a116506..ddf7eb85 100644 --- a/packages/core/src/controllers/BlockchainApiController.ts +++ b/packages/core/src/controllers/BlockchainApiController.ts @@ -3,8 +3,16 @@ import { proxy } from 'valtio'; import { CoreHelperUtil } from '../utils/CoreHelperUtil'; import { FetchUtil } from '../utils/FetchUtil'; import type { + BlockchainApiBalanceResponse, + BlockchainApiGasPriceRequest, + BlockchainApiGasPriceResponse, BlockchainApiIdentityRequest, - BlockchainApiIdentityResponse + BlockchainApiIdentityResponse, + BlockchainApiLookupEnsName, + BlockchainApiTokenPriceRequest, + BlockchainApiTokenPriceResponse, + BlockchainApiTransactionsRequest, + BlockchainApiTransactionsResponse } from '../utils/TypeUtil'; import { OptionsController } from './OptionsController'; @@ -36,6 +44,85 @@ export const BlockchainApiController = { }); }, + fetchTransactions({ + account, + projectId, + cursor, + onramp, + signal, + cache + }: BlockchainApiTransactionsRequest) { + return state.api.get({ + path: `/v1/account/${account}/history`, + params: { + projectId, + cursor, + onramp + }, + signal, + cache + }); + }, + + fetchTokenPrice({ projectId, addresses }: BlockchainApiTokenPriceRequest) { + return state.api.post({ + path: '/v1/fungible/price', + body: { + projectId, + currency: 'usd', + addresses + }, + headers: { + 'Content-Type': 'application/json' + } + }); + }, + + fetchGasPrice({ projectId, chainId }: BlockchainApiGasPriceRequest) { + const { sdkType, sdkVersion } = OptionsController.state; + + return state.api.get({ + path: `/v1/convert/gas-price`, + headers: { + 'Content-Type': 'application/json', + 'x-sdk-type': sdkType, + 'x-sdk-version': sdkVersion + }, + params: { + projectId, + chainId + } + }); + }, + + async getBalance(address: string, chainId?: string, forceUpdate?: string) { + const { sdkType, sdkVersion } = OptionsController.state; + + return state.api.get({ + path: `/v1/account/${address}/balance`, + headers: { + 'x-sdk-type': sdkType, + 'x-sdk-version': sdkVersion + }, + params: { + currency: 'usd', + projectId: OptionsController.state.projectId, + chainId, + forceUpdate + } + }); + }, + + async lookupEnsName(name: string) { + return state.api.get({ + path: `/v1/profile/account/${name}`, + params: { + projectId: OptionsController.state.projectId, + apiVersion: '2' + } + }); + }, + setClientId(clientId: string | null) { state.clientId = clientId; state.api = new FetchUtil({ baseUrl, clientId }); diff --git a/packages/core/src/controllers/ConnectionController.ts b/packages/core/src/controllers/ConnectionController.ts index 93a842e3..62faa453 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'; import { CoreHelperUtil } from '../utils/CoreHelperUtil'; import { StorageUtil } from '../utils/StorageUtil'; -import type { Connector, WcWallet } from '../utils/TypeUtil'; +import type { + Connector, + SendTransactionArgs, + WcWallet, + WriteContractArgs +} from '../utils/TypeUtil'; import { RouterController } from './RouterController'; import { ConnectorController } from './ConnectorController'; @@ -21,7 +26,13 @@ export interface ConnectionControllerClient { ) => Promise; connectExternal?: (options: ConnectExternalOptions) => Promise; signMessage: (message: string) => Promise; + sendTransaction: (args: SendTransactionArgs) => Promise<`0x${string}` | null>; + parseUnits: (value: string, decimals: number) => bigint; + formatUnits: (value: bigint, decimals: number) => string; + writeContract: (args: WriteContractArgs) => Promise<`0x${string}` | null>; disconnect: () => Promise; + getEnsAddress: (value: string) => Promise; + getEnsAvatar: (value: string) => Promise; } export interface ConnectionControllerState { @@ -122,6 +133,30 @@ export const ConnectionController = { } }, + 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); + }, + + async writeContract(args: WriteContractArgs) { + return this._getClient().writeContract(args); + }, + + async getEnsAddress(value: string) { + return this._getClient().getEnsAddress(value); + }, + + async getEnsAvatar(value: string) { + return this._getClient().getEnsAvatar(value); + }, + clearUri() { state.wcUri = undefined; state.wcPairingExpiry = undefined; @@ -142,6 +177,7 @@ export const ConnectionController = { async disconnect() { await this._getClient().disconnect(); this.resetWcConnection(); + // remove transactions RouterController.reset('Connect'); } }; diff --git a/packages/core/src/controllers/EnsController.ts b/packages/core/src/controllers/EnsController.ts new file mode 100644 index 00000000..c80fadf2 --- /dev/null +++ b/packages/core/src/controllers/EnsController.ts @@ -0,0 +1,39 @@ +import { subscribeKey as subKey } from 'valtio/vanilla/utils'; +import { proxy, subscribe as sub } from 'valtio/vanilla'; +import { BlockchainApiController } from './BlockchainApiController'; +import type { BlockchainApiEnsError } from '../utils/TypeUtil'; + +// -- Types --------------------------------------------- // + +export interface EnsControllerState { + loading: boolean; +} + +type StateKey = keyof EnsControllerState; + +// -- State --------------------------------------------- // +const state = proxy({ + loading: false +}); + +// -- Controller ---------------------------------------- // +export const EnsController = { + state, + + subscribe(callback: (newState: EnsControllerState) => void) { + return sub(state, () => callback(state)); + }, + + subscribeKey(key: K, callback: (value: EnsControllerState[K]) => void) { + return subKey(state, key, callback); + }, + + async resolveName(name: string) { + try { + return await BlockchainApiController.lookupEnsName(name); + } catch (e) { + const error = e as BlockchainApiEnsError; + throw new Error(error?.reasons?.[0]?.description || 'Error resolving name'); + } + } +}; diff --git a/packages/core/src/controllers/ModalController.ts b/packages/core/src/controllers/ModalController.ts index 3da4daf4..5b768354 100644 --- a/packages/core/src/controllers/ModalController.ts +++ b/packages/core/src/controllers/ModalController.ts @@ -34,7 +34,7 @@ export const ModalController = { if (options?.view) { RouterController.reset(options.view); } else if (AccountController.state.isConnected) { - RouterController.reset('Account'); + RouterController.reset('AccountDefault'); } else { RouterController.reset('Connect'); } diff --git a/packages/core/src/controllers/NetworkController.ts b/packages/core/src/controllers/NetworkController.ts index f917efa7..1468cba9 100644 --- a/packages/core/src/controllers/NetworkController.ts +++ b/packages/core/src/controllers/NetworkController.ts @@ -63,6 +63,12 @@ export const NetworkController = { state.approvedCaipNetworkIds = data.approvedCaipNetworkIds; }, + getApprovedCaipNetworks() { + return state.approvedCaipNetworkIds + ?.map(id => state.requestedCaipNetworks?.find(network => network.id === id)) + .filter(Boolean) as CaipNetwork[]; + }, + async switchActiveNetwork(network: NetworkControllerState['caipNetwork']) { await this._getClient().switchCaipNetwork(network); state.caipNetwork = network; diff --git a/packages/core/src/controllers/OptionsController.ts b/packages/core/src/controllers/OptionsController.ts index 6151fdd9..bd9701d6 100644 --- a/packages/core/src/controllers/OptionsController.ts +++ b/packages/core/src/controllers/OptionsController.ts @@ -83,7 +83,7 @@ export const OptionsController = { copyToClipboard(value: string) { const client = state._clipboardClient; if (client) { - client.setString(value); + client?.setString(value); } } }; diff --git a/packages/core/src/controllers/RouterController.ts b/packages/core/src/controllers/RouterController.ts index 9eecdd34..6d29bf00 100644 --- a/packages/core/src/controllers/RouterController.ts +++ b/packages/core/src/controllers/RouterController.ts @@ -2,25 +2,41 @@ import { proxy } from 'valtio'; import type { WcWallet, CaipNetwork, Connector } from '../utils/TypeUtil'; // -- Types --------------------------------------------- // +type TransactionAction = { + goBack: boolean; + view: RouterControllerState['view'] | null; + close?: boolean; + replace?: boolean; + onSuccess?: () => void; + onCancel?: () => void; +}; + export interface RouterControllerState { view: | 'Account' + | 'AccountDefault' + | 'AllWallets' | 'Connect' - | 'ConnectingWalletConnect' | 'ConnectingExternal' - | 'Networks' - | 'SwitchNetwork' - | 'AllWallets' - | 'WhatIsAWallet' - | 'WhatIsANetwork' - | 'GetWallet' + | 'ConnectingSiwe' + | 'ConnectingWalletConnect' | 'EmailVerifyDevice' | 'EmailVerifyOtp' - | 'UpdateEmailWallet' + | 'GetWallet' + | 'Networks' + | 'SwitchNetwork' + | 'Transactions' | 'UpdateEmailPrimaryOtp' | 'UpdateEmailSecondaryOtp' + | 'UpdateEmailWallet' | 'UpgradeEmailWallet' - | 'ConnectingSiwe'; + | 'WalletCompatibleNetworks' + | 'WalletReceive' + | 'WalletSend' + | 'WalletSendPreview' + | 'WalletSendSelectToken' + | 'WhatIsANetwork' + | 'WhatIsAWallet'; history: RouterControllerState['view'][]; data?: { connector?: Connector; @@ -29,12 +45,14 @@ export interface RouterControllerState { email?: string; newEmail?: string; }; + transactionStack: TransactionAction[]; } // -- State --------------------------------------------- // const state = proxy({ view: 'Connect', - history: ['Connect'] + history: ['Connect'], + transactionStack: [] }); // -- Controller ---------------------------------------- // @@ -49,6 +67,30 @@ export const RouterController = { } }, + pushTransactionStack(action: TransactionAction) { + state.transactionStack.push(action); + }, + + popTransactionStack(cancel?: boolean) { + const action = state.transactionStack.pop(); + + if (!action) { + return; + } + + if (cancel) { + this.goBack(); + action?.onCancel?.(); + } else { + if (action.goBack) { + this.goBack(); + } else if (action.view) { + this.reset(action.view); + } + action?.onSuccess?.(); + } + }, + reset(view: RouterControllerState['view']) { state.view = view; state.history = [view]; diff --git a/packages/core/src/controllers/SendController.ts b/packages/core/src/controllers/SendController.ts new file mode 100644 index 00000000..ab97b33a --- /dev/null +++ b/packages/core/src/controllers/SendController.ts @@ -0,0 +1,234 @@ +import { subscribeKey as subKey } from 'valtio/vanilla/utils'; +import { proxy, ref, subscribe as sub } from 'valtio/vanilla'; +import { ContractUtil, type Balance } from '@reown/appkit-common-react-native'; +import { AccountController } from './AccountController'; +import { ConnectionController } from './ConnectionController'; +import { SnackController } from './SnackController'; +import { CoreHelperUtil } from '../utils/CoreHelperUtil'; +import { EventsController } from './EventsController'; +import { NetworkController } from './NetworkController'; +import { RouterController } from './RouterController'; + +// -- Types --------------------------------------------- // +export interface TxParams { + receiverAddress: string; + sendTokenAmount: number; + gasPrice: bigint; + decimals: string; +} + +export interface ContractWriteParams { + receiverAddress: string; + tokenAddress: string; + sendTokenAmount: number; + decimals: string; +} + +export interface SendControllerState { + token?: Balance; + sendTokenAmount?: number; + receiverAddress?: string; + receiverProfileName?: string; + receiverProfileImageUrl?: string; + gasPrice?: bigint; + gasPriceInUSD?: number; + loading: boolean; +} + +type StateKey = keyof SendControllerState; + +// -- State --------------------------------------------- // +const state = proxy({ + loading: false +}); + +// -- Controller ---------------------------------------- // +export const SendController = { + state, + + subscribe(callback: (newState: SendControllerState) => void) { + return sub(state, () => callback(state)); + }, + + subscribeKey(key: K, callback: (value: SendControllerState[K]) => void) { + return subKey(state, key, callback); + }, + + setToken(token: SendControllerState['token']) { + if (token) { + state.token = ref(token); + } + }, + + setTokenAmount(sendTokenAmount: SendControllerState['sendTokenAmount']) { + state.sendTokenAmount = sendTokenAmount; + }, + + setReceiverAddress(receiverAddress: SendControllerState['receiverAddress']) { + state.receiverAddress = receiverAddress; + }, + + setReceiverProfileImageUrl( + receiverProfileImageUrl: SendControllerState['receiverProfileImageUrl'] + ) { + state.receiverProfileImageUrl = receiverProfileImageUrl; + }, + + setReceiverProfileName(receiverProfileName: SendControllerState['receiverProfileName']) { + state.receiverProfileName = receiverProfileName; + }, + + setGasPrice(gasPrice: SendControllerState['gasPrice']) { + state.gasPrice = gasPrice; + }, + + setGasPriceInUsd(gasPriceInUSD: SendControllerState['gasPriceInUSD']) { + state.gasPriceInUSD = gasPriceInUSD; + }, + + setLoading(loading: SendControllerState['loading']) { + state.loading = loading; + }, + + sendToken() { + if (this.state.token?.address && this.state.sendTokenAmount && this.state.receiverAddress) { + state.loading = true; + EventsController.sendEvent({ + type: 'track', + event: 'SEND_INITIATED', + properties: { + isSmartAccount: false, + token: this.state.token.address, + amount: this.state.sendTokenAmount, + network: NetworkController.state.caipNetwork?.id || '' + } + }); + this.sendERC20Token({ + receiverAddress: this.state.receiverAddress, + tokenAddress: this.state.token.address, + sendTokenAmount: this.state.sendTokenAmount, + decimals: this.state.token.quantity.decimals + }); + } else if ( + this.state.receiverAddress && + this.state.sendTokenAmount && + this.state.gasPrice && + this.state.token?.quantity.decimals + ) { + state.loading = true; + EventsController.sendEvent({ + type: 'track', + event: 'SEND_INITIATED', + properties: { + isSmartAccount: false, + token: this.state.token?.symbol, + amount: this.state.sendTokenAmount, + network: NetworkController.state.caipNetwork?.id || '' + } + }); + this.sendNativeToken({ + receiverAddress: this.state.receiverAddress, + sendTokenAmount: this.state.sendTokenAmount, + gasPrice: this.state.gasPrice, + decimals: this.state.token.quantity.decimals + }); + } + }, + + async sendNativeToken(params: TxParams) { + RouterController.pushTransactionStack({ + view: 'Account', + goBack: false + }); + + const to = params.receiverAddress as `0x${string}`; + const address = AccountController.state.address as `0x${string}`; + const value = ConnectionController.parseUnits( + params.sendTokenAmount.toString(), + Number(params.decimals) + ); + const data = '0x'; + + try { + await ConnectionController.sendTransaction({ + to, + address, + data, + value, + gasPrice: params.gasPrice + }); + SnackController.showSuccess('Transaction started'); + EventsController.sendEvent({ + type: 'track', + event: 'SEND_SUCCESS', + properties: { + isSmartAccount: false, + token: this.state.token?.symbol || '', + amount: params.sendTokenAmount, + network: NetworkController.state.caipNetwork?.id || '' + } + }); + this.resetSend(); + } catch (error) { + state.loading = false; + EventsController.sendEvent({ + type: 'track', + event: 'SEND_ERROR', + properties: { + isSmartAccount: false, + token: this.state.token?.symbol || '', + amount: params.sendTokenAmount, + network: NetworkController.state.caipNetwork?.id || '' + } + }); + SnackController.showError('Something went wrong'); + } + }, + + async sendERC20Token(params: ContractWriteParams) { + RouterController.pushTransactionStack({ + view: 'Account', + goBack: false + }); + + const amount = ConnectionController.parseUnits( + params.sendTokenAmount.toString(), + Number(params.decimals) + ); + + try { + if ( + AccountController.state.address && + params.sendTokenAmount && + params.receiverAddress && + params.tokenAddress + ) { + const tokenAddress = CoreHelperUtil.getPlainAddress( + params.tokenAddress as `${string}:${string}:${string}` + ) as `0x${string}`; + await ConnectionController.writeContract({ + fromAddress: AccountController.state.address as `0x${string}`, + tokenAddress, + receiverAddress: params.receiverAddress as `0x${string}`, + tokenAmount: amount, + method: 'transfer', + abi: ContractUtil.getERC20Abi(tokenAddress) + }); + SnackController.showSuccess('Transaction started'); + this.resetSend(); + } + } catch (error) { + state.loading = false; + SnackController.showError('Something went wrong'); + } + }, + + resetSend() { + state.token = undefined; + state.sendTokenAmount = undefined; + state.receiverAddress = undefined; + state.receiverProfileImageUrl = undefined; + state.receiverProfileName = undefined; + state.loading = false; + } +}; diff --git a/packages/core/src/controllers/SwapController.ts b/packages/core/src/controllers/SwapController.ts new file mode 100644 index 00000000..35ef8246 --- /dev/null +++ b/packages/core/src/controllers/SwapController.ts @@ -0,0 +1,108 @@ +import { subscribeKey as subKey } from 'valtio/utils'; +import { proxy, subscribe as sub } from 'valtio'; + +import { ConstantsUtil } from '../utils/ConstantsUtil'; +import { SwapApiUtil } from '../utils/SwapApiUtil'; +import { NetworkController } from './NetworkController'; +import { BlockchainApiController } from './BlockchainApiController'; +import { OptionsController } from './OptionsController'; +import { SwapCalculationUtil } from '../utils/SwapCalculationUtil'; + +// -- Constants ---------------------------------------- // +export const INITIAL_GAS_LIMIT = 150000; +export const TO_AMOUNT_DECIMALS = 6; + +// -- Types --------------------------------------------- // + +export interface SwapControllerState { + // Input values + networkPrice: string; + networkTokenSymbol: string; + + // Tokens + tokensPriceMap: Record; + + // Calculations + gasFee: string; + gasPriceInUSD?: number; +} + +type StateKey = keyof SwapControllerState; + +// -- State --------------------------------------------- // +const initialState: SwapControllerState = { + // Input values + networkPrice: '0', + networkTokenSymbol: '', + + // Tokens + tokensPriceMap: {}, + + // Calculations + gasFee: '0', + gasPriceInUSD: 0 +}; + +const state = proxy(initialState); + +// -- Controller ---------------------------------------- // +export const SwapController = { + state, + + subscribe(callback: (newState: SwapControllerState) => void) { + return sub(state, () => callback(state)); + }, + + subscribeKey(key: K, callback: (value: SwapControllerState[K]) => void) { + return subKey(state, key, callback); + }, + + getParams() { + const caipNetwork = NetworkController.state.caipNetwork; + const networkAddress = `${caipNetwork?.id}:${ConstantsUtil.NATIVE_TOKEN_ADDRESS}`; + + return { + networkAddress + }; + }, + + resetState() { + state.tokensPriceMap = initialState.tokensPriceMap; + state.networkPrice = initialState.networkPrice; + state.networkTokenSymbol = initialState.networkTokenSymbol; + }, + + //this + async getNetworkTokenPrice() { + const { networkAddress } = this.getParams(); + + const response = await BlockchainApiController.fetchTokenPrice({ + projectId: OptionsController.state.projectId, + addresses: [networkAddress] + }); + const token = response?.fungibles?.[0]; + const price = token?.price.toString() || '0'; + state.tokensPriceMap[networkAddress] = parseFloat(price); + state.networkTokenSymbol = token?.symbol || ''; + state.networkPrice = price; + }, + + //this + async getInitialGasPrice() { + const res = await SwapApiUtil.fetchGasPrice(); + + if (!res) { + return { gasPrice: null, gasPriceInUsd: null }; + } + + const value = res.standard; + const gasFee = BigInt(value); + const gasLimit = BigInt(INITIAL_GAS_LIMIT); + const gasPrice = SwapCalculationUtil.getGasPriceInUSD(state.networkPrice, gasLimit, gasFee); + + state.gasFee = value; + state.gasPriceInUSD = gasPrice; + + return { gasPrice: gasFee, gasPriceInUSD: state.gasPriceInUSD }; + } +}; diff --git a/packages/core/src/controllers/TransactionsController.ts b/packages/core/src/controllers/TransactionsController.ts new file mode 100644 index 00000000..af350299 --- /dev/null +++ b/packages/core/src/controllers/TransactionsController.ts @@ -0,0 +1,141 @@ +import type { Transaction } from '@reown/appkit-common-react-native'; +import { proxy, subscribe as sub } from 'valtio/vanilla'; +import { OptionsController } from './OptionsController'; +import { EventsController } from './EventsController'; +import { SnackController } from './SnackController'; +import { NetworkController } from './NetworkController'; +import { BlockchainApiController } from './BlockchainApiController'; + +// -- Types --------------------------------------------- // +type TransactionByMonthMap = Record; +type TransactionByYearMap = Record; + +export interface TransactionsControllerState { + transactions: Transaction[]; + loading: boolean; + empty: boolean; + next: string | undefined; +} + +// -- State --------------------------------------------- // +const state = proxy({ + transactions: [], + loading: false, + empty: false, + next: undefined +}); + +// -- Controller ---------------------------------------- // +export const TransactionsController = { + state, + + subscribe(callback: (newState: TransactionsControllerState) => void) { + return sub(state, () => callback(state)); + }, + + async fetchTransactions(accountAddress?: string, reset?: boolean) { + const { projectId } = OptionsController.state; + + if (!projectId || !accountAddress) { + throw new Error("Transactions can't be fetched without a projectId and an accountAddress"); + } + + state.loading = true; + + if (reset) { + state.next = undefined; + } + + try { + const response = await BlockchainApiController.fetchTransactions({ + account: accountAddress, + projectId, + cursor: state.next + }); + + const nonSpamTransactions = this.filterSpamTransactions(response?.data ?? []); + let filteredTransactions = [...state.transactions, ...nonSpamTransactions]; + + if (reset) { + filteredTransactions = nonSpamTransactions; + } + + state.loading = false; + + state.transactions = filteredTransactions; + + state.empty = nonSpamTransactions.length === 0; + state.next = response?.next ? response.next : undefined; + } catch (error) { + EventsController.sendEvent({ + type: 'track', + event: 'ERROR_FETCH_TRANSACTIONS', + properties: { + address: accountAddress, + projectId, + cursor: state.next, + isSmartAccount: false + } + }); + SnackController.showError('Failed to fetch transactions'); + state.loading = false; + state.empty = true; + state.next = undefined; + } + }, + + getTransactionsByYearAndMonth(transactions: Transaction[]) { + const grouped: TransactionByYearMap = {}; + let filteredTransactions = this.filterByConnectedChain(transactions); + + filteredTransactions.forEach(transaction => { + const year = new Date(transaction.metadata.minedAt).getFullYear(); + const month = new Date(transaction.metadata.minedAt).getMonth(); + + const yearTransactions = grouped[year] ?? {}; + const monthTransactions = yearTransactions[month] ?? []; + + // If there's a transaction with the same id, remove the old one + const newMonthTransactions = monthTransactions.filter(tx => tx.id !== transaction.id); + + grouped[year] = { + ...yearTransactions, + [month]: [...newMonthTransactions, transaction].sort( + (a, b) => new Date(b.metadata.minedAt).getTime() - new Date(a.metadata.minedAt).getTime() + ) + }; + }); + + return grouped; + }, + + filterSpamTransactions(transactions: Transaction[]) { + return transactions.filter(transaction => { + const isAllSpam = transaction.transfers.every( + transfer => transfer.nft_info?.flags.is_spam === true + ); + + return !isAllSpam; + }); + }, + + filterByConnectedChain(transactions: Transaction[]) { + const chainId = NetworkController.state.caipNetwork?.id; + const filteredTransactions = transactions.filter( + transaction => transaction.metadata.chain === chainId + ); + + return filteredTransactions; + }, + + clearCursor() { + state.next = undefined; + }, + + resetTransactions() { + state.transactions = []; + state.loading = false; + state.empty = false; + state.next = undefined; + } +}; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 26ce2152..3e046936 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,52 +1,63 @@ // -- Controllers ------------------------------------------------------------- -export { ModalController } from './controllers/ModalController'; -export type { ModalControllerArguments, ModalControllerState } from './controllers/ModalController'; +export { + ModalController, + type ModalControllerArguments, + type ModalControllerState +} from './controllers/ModalController'; -export { RouterController } from './controllers/RouterController'; -export type { RouterControllerState } from './controllers/RouterController'; +export { RouterController, type RouterControllerState } from './controllers/RouterController'; -export { AccountController } from './controllers/AccountController'; -export type { AccountControllerState } from './controllers/AccountController'; +export { AccountController, type AccountControllerState } from './controllers/AccountController'; -export { NetworkController } from './controllers/NetworkController'; -export type { - NetworkControllerClient, - NetworkControllerState +export { + NetworkController, + type NetworkControllerClient, + type NetworkControllerState } from './controllers/NetworkController'; -export { ConnectionController } from './controllers/ConnectionController'; -export type { - ConnectionControllerClient, - ConnectionControllerState +export { + ConnectionController, + type ConnectionControllerClient, + type ConnectionControllerState } from './controllers/ConnectionController'; -export { ConnectorController } from './controllers/ConnectorController'; -export type { ConnectorControllerState } from './controllers/ConnectorController'; +export { + ConnectorController, + type ConnectorControllerState +} from './controllers/ConnectorController'; -export { SnackController } from './controllers/SnackController'; -export type { SnackControllerState } from './controllers/SnackController'; +export { SnackController, type SnackControllerState } from './controllers/SnackController'; -export { ApiController } from './controllers/ApiController'; -export type { ApiControllerState } from './controllers/ApiController'; +export { ApiController, type ApiControllerState } from './controllers/ApiController'; -export { AssetController } from './controllers/AssetController'; -export type { AssetControllerState } from './controllers/AssetController'; +export { AssetController, type AssetControllerState } from './controllers/AssetController'; -export { ThemeController } from './controllers/ThemeController'; -export type { ThemeControllerState } from './controllers/ThemeController'; +export { ThemeController, type ThemeControllerState } from './controllers/ThemeController'; -export { OptionsController } from './controllers/OptionsController'; -export type { OptionsControllerState } from './controllers/OptionsController'; +export { OptionsController, type OptionsControllerState } from './controllers/OptionsController'; -export { PublicStateController } from './controllers/PublicStateController'; -export type { PublicStateControllerState } from './controllers/PublicStateController'; +export { + PublicStateController, + type PublicStateControllerState +} from './controllers/PublicStateController'; export { BlockchainApiController } from './controllers/BlockchainApiController'; -export { EventsController } from './controllers/EventsController'; -export type { EventsControllerState } from './controllers/EventsController'; +export { SwapController, type SwapControllerState } from './controllers/SwapController'; + +export { EventsController, type EventsControllerState } from './controllers/EventsController'; + +export { EnsController, type EnsControllerState } from './controllers/EnsController'; + +export { + TransactionsController, + type TransactionsControllerState +} from './controllers/TransactionsController'; + +export { SendController, type SendControllerState } from './controllers/SendController'; // -- Utils ------------------------------------------------------------------- +export { ApiUtil } from './utils/ApiUtil'; export { AssetUtil } from './utils/AssetUtil'; export { ConstantsUtil } from './utils/ConstantsUtil'; export { CoreHelperUtil } from './utils/CoreHelperUtil'; diff --git a/packages/core/src/utils/ApiUtil.ts b/packages/core/src/utils/ApiUtil.ts new file mode 100644 index 00000000..cf689514 --- /dev/null +++ b/packages/core/src/utils/ApiUtil.ts @@ -0,0 +1,18 @@ +import { Platform } from 'react-native'; +import { CoreHelperUtil } from './CoreHelperUtil'; + +export const ApiUtil = { + getOrigin() { + return CoreHelperUtil.getBundleId(); + }, + + getUserAgent() { + const reactNativeVersion = [ + Platform.constants.reactNativeVersion.major, + Platform.constants.reactNativeVersion.minor, + Platform.constants.reactNativeVersion.patch + ].join('.'); + + return `${Platform.OS}-${Platform.Version}@rn-${reactNativeVersion}`; + } +}; diff --git a/packages/core/src/utils/ConstantsUtil.ts b/packages/core/src/utils/ConstantsUtil.ts index 0264c7ba..288692aa 100644 --- a/packages/core/src/utils/ConstantsUtil.ts +++ b/packages/core/src/utils/ConstantsUtil.ts @@ -7,5 +7,7 @@ export const ConstantsUtil = { EMAIL_REGEX: /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)+$/, - LINKING_ERROR: 'LINKING_ERROR' + LINKING_ERROR: 'LINKING_ERROR', + + NATIVE_TOKEN_ADDRESS: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee' }; diff --git a/packages/core/src/utils/CoreHelperUtil.ts b/packages/core/src/utils/CoreHelperUtil.ts index 5425cb26..99eb1255 100644 --- a/packages/core/src/utils/CoreHelperUtil.ts +++ b/packages/core/src/utils/CoreHelperUtil.ts @@ -1,7 +1,7 @@ /* eslint-disable no-bitwise */ import { Linking, Platform } from 'react-native'; -import { ConstantsUtil as CommonConstants } from '@reown/appkit-common-react-native'; +import { ConstantsUtil as CommonConstants, type Balance } from '@reown/appkit-common-react-native'; import { ConstantsUtil } from './ConstantsUtil'; import type { CaipAddress, DataWallet, LinkingRecord } from './TypeUtil'; @@ -157,6 +157,27 @@ export const CoreHelperUtil = { return formattedBalance ? `${formattedBalance} ${symbol}` : `0.000 ${symbol || ''}`; }, + isAddress(address: string, chain = 'eip155'): boolean { + switch (chain) { + case 'eip155': + if (!/^(?:0x)?[0-9a-f]{40}$/iu.test(address)) { + return false; + } else if ( + /^(?:0x)?[0-9a-f]{40}$/iu.test(address) || + /^(?:0x)?[0-9A-F]{40}$/iu.test(address) + ) { + return true; + } + + return false; + case 'solana': + return /[1-9A-HJ-NP-Za-km-z]{32,44}$/iu.test(address); + + default: + return false; + } + }, + getApiUrl() { return CommonConstants.API_URL; }, @@ -245,5 +266,21 @@ export const CoreHelperUtil = { .catch(reason => ({ status: 'rejected', reason })) ) ); + }, + + calculateAndFormatBalance(array?: Balance[]) { + if (!array?.length) { + return { dollars: '0', pennies: '00' }; + } + + let sum = 0; + for (const item of array) { + sum += item.value ?? 0; + } + + const roundedNumber = sum.toFixed(2); + const [dollars, pennies] = roundedNumber.split('.'); + + return { dollars, pennies }; } }; diff --git a/packages/core/src/utils/FetchUtil.ts b/packages/core/src/utils/FetchUtil.ts index 99c0df37..b4d6d805 100644 --- a/packages/core/src/utils/FetchUtil.ts +++ b/packages/core/src/utils/FetchUtil.ts @@ -1,3 +1,5 @@ +import type { RequestCache } from './TypeUtil'; + // -- Types ---------------------------------------------------------------------- interface Options { baseUrl: string; @@ -8,6 +10,8 @@ interface RequestArguments { path: string; headers?: HeadersInit_; params?: Record; + cache?: RequestCache; + signal?: AbortSignal; } interface PostArguments extends RequestArguments { diff --git a/packages/core/src/utils/SwapApiUtil.ts b/packages/core/src/utils/SwapApiUtil.ts new file mode 100644 index 00000000..3c47f65c --- /dev/null +++ b/packages/core/src/utils/SwapApiUtil.ts @@ -0,0 +1,19 @@ +import { BlockchainApiController } from '../controllers/BlockchainApiController'; +import { OptionsController } from '../controllers/OptionsController'; +import { NetworkController } from '../controllers/NetworkController'; + +export const SwapApiUtil = { + async fetchGasPrice() { + const projectId = OptionsController.state.projectId; + const caipNetwork = NetworkController.state.caipNetwork; + + if (!caipNetwork) { + return null; + } + + return await BlockchainApiController.fetchGasPrice({ + projectId, + chainId: caipNetwork.id + }); + } +}; diff --git a/packages/core/src/utils/SwapCalculationUtil.ts b/packages/core/src/utils/SwapCalculationUtil.ts new file mode 100644 index 00000000..b946c656 --- /dev/null +++ b/packages/core/src/utils/SwapCalculationUtil.ts @@ -0,0 +1,21 @@ +// -- Types --------------------------------------------- // + +import { NumberUtil } from '@reown/appkit-common-react-native'; + +// -- Util ---------------------------------------- // +export const SwapCalculationUtil = { + getGasPriceInEther(gas: bigint, gasPrice: bigint) { + const totalGasCostInWei = gasPrice * gas; + const totalGasCostInEther = Number(totalGasCostInWei) / 1e18; + + return totalGasCostInEther; + }, + + getGasPriceInUSD(networkPrice: string, gas: bigint, gasPrice: bigint) { + const totalGasCostInEther = SwapCalculationUtil.getGasPriceInEther(gas, gasPrice); + const networkPriceInUSD = NumberUtil.bigNumber(networkPrice); + const gasCostInUSD = networkPriceInUSD.multipliedBy(totalGasCostInEther); + + return gasCostInUSD.toNumber(); + } +}; diff --git a/packages/core/src/utils/TypeUtil.ts b/packages/core/src/utils/TypeUtil.ts index 6a5154f8..4b380d0b 100644 --- a/packages/core/src/utils/TypeUtil.ts +++ b/packages/core/src/utils/TypeUtil.ts @@ -1,3 +1,9 @@ +import type { Balance, Transaction } from '@reown/appkit-common-react-native'; + +export interface BaseError { + message?: string; +} + export type CaipAddress = `${string}:${string}:${string}`; export type CaipNetworkId = `${string}:${string}`; @@ -98,6 +104,16 @@ export interface ApiGetAnalyticsConfigResponse { isAnalyticsEnabled: boolean; } +export type RequestCache = + | 'default' + | 'force-cache' + | 'no-cache' + | 'no-store' + | 'only-if-cached' + | 'reload'; + +// -- ThemeController Types --------------------------------------------------- + export type ThemeMode = 'dark' | 'light'; export interface ThemeVariables { @@ -114,6 +130,74 @@ export interface BlockchainApiIdentityResponse { name: string; } +export interface BlockchainApiBalanceResponse { + balances: Balance[]; +} + +export interface BlockchainApiTransactionsRequest { + account: string; + projectId: string; + cursor?: string; + onramp?: 'coinbase'; + signal?: AbortSignal; + cache?: RequestCache; +} + +export interface BlockchainApiTransactionsResponse { + data: Transaction[]; + next: string | null; +} + +export interface BlockchainApiTokenPriceRequest { + projectId: string; + currency?: 'usd' | 'eur' | 'gbp' | 'aud' | 'cad' | 'inr' | 'jpy' | 'btc' | 'eth'; + addresses: string[]; +} + +export interface BlockchainApiTokenPriceResponse { + fungibles: { + name: string; + symbol: string; + iconUrl: string; + price: number; + }[]; +} + +export interface BlockchainApiGasPriceRequest { + projectId: string; + chainId: string; +} + +export interface BlockchainApiGasPriceResponse { + standard: string; + fast: string; + instant: string; +} + +export interface BlockchainApiEnsError extends BaseError { + status: string; + reasons: { name: string; description: string }[]; +} + +export type ReownName = `${string}.reown.id` | `${string}.wcn.id`; + +export interface BlockchainApiLookupEnsName { + name: ReownName; + registered: number; + updated: number; + addresses: Record< + string, + { + address: string; + created: string; + } + >; + attributes: { + avatar?: string; + bio?: string; + }[]; +} + // -- OptionsController Types --------------------------------------------------- export interface Token { address: string; @@ -284,8 +368,93 @@ export type Event = | { type: 'track'; event: 'SIWE_AUTH_ERROR'; + } + | { + type: 'track'; + event: 'CLICK_TRANSACTIONS'; + properties: { + isSmartAccount: boolean; + }; + } + | { + type: 'track'; + event: 'ERROR_FETCH_TRANSACTIONS'; + properties: { + address: string; + projectId: string; + cursor: string | undefined; + isSmartAccount: boolean; + }; + } + | { + type: 'track'; + event: 'LOAD_MORE_TRANSACTIONS'; + properties: { + address: string | undefined; + projectId: string; + cursor: string | undefined; + isSmartAccount: boolean; + }; + } + | { + type: 'track'; + event: 'OPEN_SEND'; + properties: { + isSmartAccount: boolean; + network: string; + }; + } + | { + type: 'track'; + event: 'SEND_INITIATED'; + properties: { + isSmartAccount: boolean; + network: string; + token: string; + amount: number; + }; + } + | { + type: 'track'; + event: 'SEND_SUCCESS'; + properties: { + isSmartAccount: boolean; + network: string; + token: string; + amount: number; + }; + } + | { + type: 'track'; + event: 'SEND_ERROR'; + properties: { + isSmartAccount: boolean; + network: string; + token: string; + amount: number; + }; }; +// -- Send Controller Types ------------------------------------- + +export interface SendTransactionArgs { + to: `0x${string}`; + data: `0x${string}`; + value: bigint; + gas?: bigint; + gasPrice: bigint; + address: `0x${string}`; +} + +export interface WriteContractArgs { + receiverAddress: `0x${string}`; + tokenAmount: bigint; + tokenAddress: `0x${string}`; + fromAddress: `0x${string}`; + method: 'send' | 'transfer' | 'call'; + abi: any; +} + // -- Email Types ------------------------------------------------ /** * Matches type defined for packages/email/src/AppKitFrameProvider.ts diff --git a/packages/ethers/src/client.ts b/packages/ethers/src/client.ts index 019d6483..284585ba 100644 --- a/packages/ethers/src/client.ts +++ b/packages/ethers/src/client.ts @@ -1,10 +1,15 @@ import { + BrowserProvider, + Contract, InfuraProvider, JsonRpcProvider, + JsonRpcSigner, formatEther, + formatUnits, getAddress, hexlify, isHexString, + parseUnits, toUtf8Bytes } from 'ethers'; import { @@ -16,10 +21,12 @@ import { type LibraryOptions, type NetworkControllerClient, type PublicStateControllerState, + type SendTransactionArgs, type Token, - AppKitScaffold + AppKitScaffold, + type WriteContractArgs } from '@reown/appkit-scaffold-react-native'; -import { NetworkUtil } from '@reown/appkit-common-react-native'; +import { NamesUtil, NetworkUtil } from '@reown/appkit-common-react-native'; import { ConstantsUtil, PresetsUtil, @@ -275,6 +282,101 @@ export class AppKit extends AppKitScaffold { }); return signature as `0x${string}`; + }, + + parseUnits: (value: string, decimals: number) => parseUnits(value, decimals), + + formatUnits: (value: bigint, decimals: number) => formatUnits(value, decimals), + + sendTransaction: async (data: SendTransactionArgs) => { + const { chainId, provider, address } = EthersStoreUtil.state; + + if (!provider) { + throw new Error('ethersClient:sendTransaction - provider is undefined'); + } + + if (!address) { + throw new Error('ethersClient: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 as `0x${string}`) || null; + }, + + writeContract: async (data: WriteContractArgs) => { + const { chainId, provider, address } = EthersStoreUtil.state; + + if (!provider) { + throw new Error('ethersClient:writeContract - provider is undefined'); + } + + if (!address) { + throw new Error('ethersClient:writeContract - address is undefined'); + } + + const browserProvider = new BrowserProvider(provider, chainId); + const signer = new JsonRpcSigner(browserProvider, address); + const contract = new Contract(data.tokenAddress, data.abi, signer); + + if (!contract || !data.method) { + throw new Error('Contract method is undefined'); + } + + const method = contract[data.method]; + if (method) { + const tx = await method(data.receiverAddress, data.tokenAmount); + + return tx; + } + + throw new Error('Contract method is undefined'); + }, + + getEnsAddress: async (value: string) => { + try { + const chainId = Number(this.getCaipNetwork()?.id); + let ensName: string | null = null; + let wcName: boolean | string = false; + + if (NamesUtil.isReownName(value)) { + wcName = (await this?.resolveReownName(value)) || false; + } + + // If on mainnet, fetch from ENS + if (chainId === 1) { + const ensProvider = new InfuraProvider('mainnet'); + ensName = await ensProvider.resolveName(value); + } + + return ensName || wcName || false; + } catch { + return false; + } + }, + + getEnsAvatar: async (value: string) => { + const chainId = Number(NetworkUtil.caipNetworkIdToNumber(this.getCaipNetwork()?.id)); + if (chainId === 1) { + const ensProvider = new InfuraProvider('mainnet'); + const avatar = await ensProvider.getAvatar(value); + + return avatar || false; + } + + return false; } }; diff --git a/packages/ethers5/src/client.ts b/packages/ethers5/src/client.ts index af98e7e1..2b0af1c3 100644 --- a/packages/ethers5/src/client.ts +++ b/packages/ethers5/src/client.ts @@ -1,4 +1,4 @@ -import { ethers, utils } from 'ethers'; +import { Contract, ethers, utils } from 'ethers'; import { type CaipAddress, type CaipNetwork, @@ -8,7 +8,9 @@ import { type LibraryOptions, type NetworkControllerClient, type PublicStateControllerState, + type SendTransactionArgs, type Token, + type WriteContractArgs, AppKitScaffold } from '@reown/appkit-scaffold-react-native'; import { @@ -28,7 +30,7 @@ import { type CombinedProviderType, type AppKitFrameProvider } from '@reown/appkit-scaffold-utils-react-native'; -import { NetworkUtil } from '@reown/appkit-common-react-native'; +import { NamesUtil, NetworkUtil } from '@reown/appkit-common-react-native'; import EthereumProvider, { OPTIONAL_METHODS } from '@walletconnect/ethereum-provider'; import type { EthereumProviderOptions } from '@walletconnect/ethereum-provider'; @@ -268,6 +270,95 @@ export class AppKit extends AppKitScaffold { }); 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 (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 (txReceipt?.blockHash as `0x${string}`) || null; + }, + + writeContract: async (data: WriteContractArgs) => { + const { chainId, provider, address } = EthersStoreUtil.state; + if (!provider) { + throw new Error('writeContract - provider is undefined'); + } + if (!address) { + throw new Error('writeContract - address is undefined'); + } + const browserProvider = new ethers.providers.Web3Provider(provider, chainId); + const signer = browserProvider.getSigner(address); + const contract = new Contract(data.tokenAddress, data.abi, signer); + if (!contract || !data.method) { + throw new Error('Contract method is undefined'); + } + const method = contract[data.method]; + if (method) { + return await method(data.receiverAddress, data.tokenAmount); + } + throw new Error('Contract method is undefined'); + }, + + getEnsAddress: async (value: string) => { + try { + const { chainId } = EthersStoreUtil.state; + let ensName: string | null = null; + let wcName: boolean | string = false; + + if (NamesUtil.isReownName(value)) { + wcName = (await this.resolveReownName(value)) || false; + } + + if (chainId === 1) { + const ensProvider = new ethers.providers.InfuraProvider('mainnet'); + ensName = await ensProvider.resolveName(value); + } + + return ensName || wcName || false; + } catch { + return false; + } + }, + + getEnsAvatar: async (value: string) => { + const { chainId } = EthersStoreUtil.state; + if (chainId === 1) { + const ensProvider = new ethers.providers.InfuraProvider('mainnet'); + const avatar = await ensProvider.getAvatar(value); + + return avatar || false; + } + + return false; } }; diff --git a/packages/scaffold/src/client.ts b/packages/scaffold/src/client.ts index e775b3cf..66760372 100644 --- a/packages/scaffold/src/client.ts +++ b/packages/scaffold/src/client.ts @@ -21,13 +21,15 @@ import { BlockchainApiController, ConnectionController, ConnectorController, + EnsController, EventsController, ModalController, NetworkController, OptionsController, PublicStateController, StorageUtil, - ThemeController + ThemeController, + TransactionsController } from '@reown/appkit-core-react-native'; import { ConstantsUtil } from '@reown/appkit-common-react-native'; @@ -134,6 +136,15 @@ export class AppKitScaffold { return EventsController.subscribe(callback); } + public resolveReownName = async (name: string) => { + const wcNameAddress = await EnsController.resolveName(name); + const networkNameAddresses = wcNameAddress?.addresses + ? Object.values(wcNameAddress?.addresses) + : []; + + return networkNameAddresses[0]?.address || false; + }; + // -- Protected ---------------------------------------------------------------- protected setIsConnected: (typeof AccountController)['setIsConnected'] = isConnected => { AccountController.setIsConnected(isConnected); @@ -143,6 +154,8 @@ export class AppKitScaffold { AccountController.setCaipAddress(caipAddress); }; + protected getCaipAddress = () => AccountController.state.caipAddress; + protected setBalance: (typeof AccountController)['setBalance'] = (balance, balanceSymbol) => { AccountController.setBalance(balance, balanceSymbol); }; @@ -193,6 +206,7 @@ export class AppKitScaffold { protected resetWcConnection: (typeof ConnectionController)['resetWcConnection'] = () => { ConnectionController.resetWcConnection(); + TransactionsController.resetTransactions(); }; protected fetchIdentity: (typeof BlockchainApiController)['fetchIdentity'] = request => diff --git a/packages/scaffold/src/hooks/useDebounceCallback.ts b/packages/scaffold/src/hooks/useDebounceCallback.ts index 8708750c..70be71f3 100644 --- a/packages/scaffold/src/hooks/useDebounceCallback.ts +++ b/packages/scaffold/src/hooks/useDebounceCallback.ts @@ -1,7 +1,7 @@ import { useCallback, useEffect, useRef } from 'react'; interface Props { - callback: (args: any) => void; + callback: ((args: any) => any) | ((args: any) => Promise); delay?: number; } diff --git a/packages/scaffold/src/modal/w3m-account-button/index.tsx b/packages/scaffold/src/modal/w3m-account-button/index.tsx index d6fe2907..4f318640 100644 --- a/packages/scaffold/src/modal/w3m-account-button/index.tsx +++ b/packages/scaffold/src/modal/w3m-account-button/index.tsx @@ -34,8 +34,8 @@ export function AccountButton({ balance, disabled, style, testID }: AccountButto return ( c.type === 'AUTH')?.provider as AppKitFrameProvider; - const modalCoverScreen = activeView !== 'ConnectingSiwe'; + const enableCoverScreen = !viewsAboveModal.includes(activeView); const AuthView = authProvider?.AuthView; const onBackButtonPress = () => { @@ -66,8 +69,11 @@ export function AppKit() { return; } + const newAddress = CoreHelperUtil.getPlainAddress(address); + TransactionsController.resetTransactions(); + TransactionsController.fetchTransactions(newAddress); + if (isSiweEnabled) { - const newAddress = CoreHelperUtil.getPlainAddress(address); const newNetworkId = CoreHelperUtil.getNetworkId(address); const { SIWEController } = await import('@reown/appkit-siwe-react-native'); const { signOutOnAccountChange, signOutOnNetworkChange } = @@ -115,7 +121,7 @@ export function AppKit() { <> { switch (view) { - case 'Connect': - return ConnectView; + case 'Account': + return AccountView; + case 'AccountDefault': + return AccountDefaultView; case 'AllWallets': return AllWalletsView; - case 'ConnectingWalletConnect': - return ConnectingView; + case 'Connect': + return ConnectView; case 'ConnectingExternal': return ConnectingExternalView; - case 'WhatIsAWallet': - return WhatIsAWalletView; - case 'WhatIsANetwork': - return WhatIsNetworkView; + case 'ConnectingSiwe': + return ConnectingSiweView; + case 'ConnectingWalletConnect': + return ConnectingView; + case 'EmailVerifyDevice': + return EmailVerifyDeviceView; + case 'EmailVerifyOtp': + return EmailVerifyOtpView; case 'GetWallet': return GetWalletView; case 'Networks': return NetworksView; case 'SwitchNetwork': return NetworkSwitchView; - case 'Account': - return AccountView; - case 'EmailVerifyDevice': - return EmailVerifyDeviceView; - case 'EmailVerifyOtp': - return EmailVerifyOtpView; - case 'UpdateEmailWallet': - return UpdateEmailWalletView; + case 'Transactions': + return TransactionsView; case 'UpdateEmailPrimaryOtp': return UpdateEmailPrimaryOtpView; case 'UpdateEmailSecondaryOtp': return UpdateEmailSecondaryOtpView; + case 'UpdateEmailWallet': + return UpdateEmailWalletView; case 'UpgradeEmailWallet': return UpgradeEmailWalletView; - case 'ConnectingSiwe': - return ConnectingSiweView; + case 'WalletCompatibleNetworks': + return WalletCompatibleNetworks; + case 'WalletReceive': + return WalletReceiveView; + case 'WalletSend': + return WalletSendView; + case 'WalletSendPreview': + return WalletSendPreviewView; + case 'WalletSendSelectToken': + return WalletSendSelectTokenView; + case 'WhatIsANetwork': + return WhatIsNetworkView; + case 'WhatIsAWallet': + return WhatIsAWalletView; default: return ConnectView; } diff --git a/packages/scaffold/src/partials/w3m-account-activity/index.tsx b/packages/scaffold/src/partials/w3m-account-activity/index.tsx new file mode 100644 index 00000000..f2ee5cb8 --- /dev/null +++ b/packages/scaffold/src/partials/w3m-account-activity/index.tsx @@ -0,0 +1,161 @@ +import { useCallback, useMemo, useState } from 'react'; +import { useSnapshot } from 'valtio'; +import { ScrollView, View, type StyleProp, type ViewStyle, RefreshControl } from 'react-native'; +import { + FlexView, + Link, + ListTransaction, + LoadingSpinner, + Text, + TransactionUtil, + useTheme +} from '@reown/appkit-ui-react-native'; +import { type Transaction, type TransactionImage } from '@reown/appkit-common-react-native'; +import { + AccountController, + AssetUtil, + EventsController, + NetworkController, + OptionsController, + TransactionsController +} from '@reown/appkit-core-react-native'; +import { AccountPlaceholder } from '../w3m-account-placeholder'; +import { getTransactionListItemProps } from './utils'; +import styles from './styles'; + +interface Props { + style?: StyleProp; +} + +export function AccountActivity({ style }: Props) { + const Theme = useTheme(); + const [refreshing, setRefreshing] = useState(false); + const { loading, transactions, next } = useSnapshot(TransactionsController.state); + const { caipNetwork } = useSnapshot(NetworkController.state); + const networkImage = AssetUtil.getNetworkImage(caipNetwork); + + const handleLoadMore = () => { + TransactionsController.fetchTransactions(AccountController.state.address); + EventsController.sendEvent({ + type: 'track', + event: 'LOAD_MORE_TRANSACTIONS', + properties: { + address: AccountController.state.address, + projectId: OptionsController.state.projectId, + cursor: TransactionsController.state.next, + isSmartAccount: false + } + }); + }; + + const onRefresh = useCallback(async () => { + setRefreshing(true); + await TransactionsController.fetchTransactions(AccountController.state.address, true); + setRefreshing(false); + }, []); + + const transactionsByYear = useMemo(() => { + return TransactionsController.getTransactionsByYearAndMonth(transactions as Transaction[]); + }, [transactions]); + + if (loading && !transactions.length) { + return ( + + + + ); + } + + if (!Object.keys(transactionsByYear).length) { + return ( + + + + ); + } + + return ( + + } + > + {Object.keys(transactionsByYear) + .reverse() + .map(year => ( + + {Object.keys(transactionsByYear[year] || {}) + .reverse() + .map(month => ( + + + {TransactionUtil.getTransactionGroupTitle(year, month)} + + {transactionsByYear[year]?.[month]?.map((transaction: Transaction) => { + const { date, type, descriptions, status, images, isAllNFT, transfers } = + getTransactionListItemProps(transaction); + const hasMultipleTransfers = transfers?.length > 2; + + if (hasMultipleTransfers) { + return transfers.map((transfer, index) => { + const description = TransactionUtil.getTransferDescription(transfer); + + return ( + + ); + }); + } + + return ( + + ); + })} + + ))} + + ))} + {(next || loading) && ( + + {next && !loading && ( + + Load more + + )} + {loading && !refreshing && } + + )} + + ); +} diff --git a/packages/scaffold/src/partials/w3m-account-activity/styles.ts b/packages/scaffold/src/partials/w3m-account-activity/styles.ts new file mode 100644 index 00000000..df9874a4 --- /dev/null +++ b/packages/scaffold/src/partials/w3m-account-activity/styles.ts @@ -0,0 +1,28 @@ +import { Spacing } from '@reown/appkit-ui-react-native'; +import { StyleSheet } from 'react-native'; + +export default StyleSheet.create({ + container: { + paddingHorizontal: Spacing.xs + }, + contentContainer: { + paddingBottom: Spacing.m + }, + separatorText: { + marginVertical: Spacing.xs + }, + transactionItem: { + marginVertical: Spacing.xs + }, + footer: { + height: 40 + }, + placeholder: { + minHeight: 200 + }, + loadMoreButton: { + alignSelf: 'center', + width: 100, + marginVertical: Spacing.xs + } +}); diff --git a/packages/scaffold/src/partials/w3m-account-activity/utils.ts b/packages/scaffold/src/partials/w3m-account-activity/utils.ts new file mode 100644 index 00000000..be865523 --- /dev/null +++ b/packages/scaffold/src/partials/w3m-account-activity/utils.ts @@ -0,0 +1,25 @@ +import { DateUtil, type Transaction } from '@reown/appkit-common-react-native'; +import { TransactionUtil } from '@reown/appkit-ui-react-native'; +import type { TransactionType } from '@reown/appkit-ui-react-native/lib/typescript/utils/TypesUtil'; + +export function getTransactionListItemProps(transaction: Transaction) { + const date = DateUtil.formatDate(transaction?.metadata?.minedAt); + const descriptions = TransactionUtil.getTransactionDescriptions(transaction); + + const transfers = transaction?.transfers; + const transfer = transaction?.transfers?.[0]; + const isAllNFT = + Boolean(transfer) && transaction?.transfers?.every(item => Boolean(item.nft_info)); + const images = TransactionUtil.getTransactionImages(transfers); + + return { + date, + direction: transfer?.direction, + descriptions, + isAllNFT, + images, + status: transaction.metadata?.status, + transfers, + type: transaction.metadata?.operationType as TransactionType + }; +} diff --git a/packages/scaffold/src/partials/w3m-account-nfts/index.tsx b/packages/scaffold/src/partials/w3m-account-nfts/index.tsx new file mode 100644 index 00000000..59643cab --- /dev/null +++ b/packages/scaffold/src/partials/w3m-account-nfts/index.tsx @@ -0,0 +1,11 @@ +import { AccountPlaceholder } from '../w3m-account-placeholder'; + +export function AccountNfts() { + return ( + + ); +} diff --git a/packages/scaffold/src/partials/w3m-account-placeholder/index.tsx b/packages/scaffold/src/partials/w3m-account-placeholder/index.tsx new file mode 100644 index 00000000..63db85b1 --- /dev/null +++ b/packages/scaffold/src/partials/w3m-account-placeholder/index.tsx @@ -0,0 +1,35 @@ +import { StyleSheet } from 'react-native'; +import { IconBox, Text, FlexView, Spacing, type IconType } from '@reown/appkit-ui-react-native'; + +interface Props { + icon: IconType; + title: string; + description: string; +} + +export function AccountPlaceholder({ icon, title, description }: Props) { + return ( + + + + {title} + + + {description} + + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1 + }, + title: { + marginTop: Spacing.xl, + marginBottom: Spacing.xs + }, + description: { + maxWidth: '50%' + } +}); diff --git a/packages/scaffold/src/partials/w3m-account-tokens/index.tsx b/packages/scaffold/src/partials/w3m-account-tokens/index.tsx new file mode 100644 index 00000000..bbc8b797 --- /dev/null +++ b/packages/scaffold/src/partials/w3m-account-tokens/index.tsx @@ -0,0 +1,74 @@ +import { useCallback, useState } from 'react'; +import { RefreshControl, ScrollView, type StyleProp, type ViewStyle } from 'react-native'; +import { useSnapshot } from 'valtio'; +import { + AccountController, + AssetUtil, + NetworkController, + RouterController +} from '@reown/appkit-core-react-native'; +import { FlexView, ListItem, Text, ListToken, useTheme } from '@reown/appkit-ui-react-native'; + +interface Props { + style?: StyleProp; +} + +export function AccountTokens({ style }: Props) { + const Theme = useTheme(); + const [refreshing, setRefreshing] = useState(false); + const { tokenBalance } = useSnapshot(AccountController.state); + const { caipNetwork } = useSnapshot(NetworkController.state); + const networkImage = AssetUtil.getNetworkImage(caipNetwork); + + const onRefresh = useCallback(async () => { + setRefreshing(true); + AccountController.fetchTokenBalance(); + setRefreshing(false); + }, []); + + const onReceivePress = () => { + RouterController.push('WalletReceive'); + }; + + if (!tokenBalance?.length) { + return ( + + + + Receive funds + + + Transfer tokens on your wallet + + + + ); + } + + return ( + + } + > + {tokenBalance.map(token => ( + + ))} + + ); +} diff --git a/packages/scaffold/src/partials/w3m-account-wallet-features/index.tsx b/packages/scaffold/src/partials/w3m-account-wallet-features/index.tsx new file mode 100644 index 00000000..b9a35a3a --- /dev/null +++ b/packages/scaffold/src/partials/w3m-account-wallet-features/index.tsx @@ -0,0 +1,99 @@ +import { useState } from 'react'; +import { useSnapshot } from 'valtio'; +import { Balance, FlexView, IconLink, Tabs } from '@reown/appkit-ui-react-native'; +import { + AccountController, + CoreHelperUtil, + EventsController, + NetworkController, + RouterController +} from '@reown/appkit-core-react-native'; +import type { Balance as BalanceType } from '@reown/appkit-common-react-native'; +import { AccountNfts } from '../w3m-account-nfts'; +import { AccountActivity } from '../w3m-account-activity'; +import { AccountTokens } from '../w3m-account-tokens'; +import styles from './styles'; + +export interface AccountWalletFeaturesProps { + value: string; +} + +export function AccountWalletFeatures() { + const [activeTab, setActiveTab] = useState(0); + const { tokenBalance } = useSnapshot(AccountController.state); + const balance = CoreHelperUtil.calculateAndFormatBalance(tokenBalance as BalanceType[]); + + const onTabChange = (index: number) => { + setActiveTab(index); + if (index === 2) { + onTransactionsPress(); + } + }; + + const onTransactionsPress = () => { + EventsController.sendEvent({ + type: 'track', + event: 'CLICK_TRANSACTIONS', + properties: { + isSmartAccount: false + } + }); + }; + + const onSendPress = () => { + EventsController.sendEvent({ + type: 'track', + event: 'OPEN_SEND', + properties: { + network: NetworkController.state.caipNetwork?.id || '', + isSmartAccount: false + } + }); + RouterController.push('WalletSend'); + }; + + const onReceivePress = () => { + RouterController.push('WalletReceive'); + }; + + return ( + + + + + + + + + + + {activeTab === 0 && } + {activeTab === 1 && } + {activeTab === 2 && } + + + ); +} diff --git a/packages/scaffold/src/partials/w3m-account-wallet-features/styles.ts b/packages/scaffold/src/partials/w3m-account-wallet-features/styles.ts new file mode 100644 index 00000000..3f8f34af --- /dev/null +++ b/packages/scaffold/src/partials/w3m-account-wallet-features/styles.ts @@ -0,0 +1,38 @@ +import { Spacing } from '@reown/appkit-ui-react-native'; +import { StyleSheet } from 'react-native'; + +export default StyleSheet.create({ + container: { + height: 400 + }, + balanceText: { + fontSize: 40, + fontWeight: '500' + }, + actionsContainer: { + width: '100%', + marginTop: Spacing.s, + marginBottom: Spacing.l + }, + action: { + flex: 1, + height: 52 + }, + actionLeft: { + marginRight: 8 + }, + actionRight: { + marginLeft: 8 + }, + tab: { + width: '100%', + paddingHorizontal: Spacing.s + }, + tabContainer: { + flex: 1, + width: '100%' + }, + tabContent: { + paddingHorizontal: Spacing.m + } +}); diff --git a/packages/scaffold/src/partials/w3m-connecting-header/index.tsx b/packages/scaffold/src/partials/w3m-connecting-header/index.tsx index 8bfdc5b7..45a11931 100644 --- a/packages/scaffold/src/partials/w3m-connecting-header/index.tsx +++ b/packages/scaffold/src/partials/w3m-connecting-header/index.tsx @@ -1,22 +1,31 @@ import type { Platform } from '@reown/appkit-core-react-native'; -import { FlexView, Tabs } from '@reown/appkit-ui-react-native'; +import { FlexView, Tabs, type IconType } from '@reown/appkit-ui-react-native'; +import { StyleSheet } from 'react-native'; export interface ConnectingHeaderProps { platforms: Platform[]; onSelectPlatform: (platform: Platform) => void; } +interface Tab { + label: string; + icon: IconType; + platform: Platform; +} + export function ConnectingHeader({ platforms, onSelectPlatform }: ConnectingHeaderProps) { const generateTabs = () => { - const tabs = platforms.map(platform => { - if (platform === 'mobile') { - return { label: 'Mobile', icon: 'mobile', platform: 'mobile' } as const; - } else if (platform === 'web') { - return { label: 'Web', icon: 'browser', platform: 'web' } as const; - } else { - return { label: 'QR Code', icon: 'qrCode', platform: 'qrcode' } as const; - } - }); + const tabs = platforms + .map(platform => { + if (platform === 'mobile') { + return { label: 'Mobile', icon: 'mobile', platform: 'mobile' } as const; + } else if (platform === 'web') { + return { label: 'Web', icon: 'browser', platform: 'web' } as const; + } else { + return undefined; + } + }) + .filter(Boolean) as Tab[]; return tabs; }; @@ -32,7 +41,13 @@ export function ConnectingHeader({ platforms, onSelectPlatform }: ConnectingHead return ( - + ); } + +const styles = StyleSheet.create({ + tab: { + maxWidth: '50%' + } +}); diff --git a/packages/scaffold/src/partials/w3m-header/index.tsx b/packages/scaffold/src/partials/w3m-header/index.tsx index b7d665b3..a8fb15f9 100644 --- a/packages/scaffold/src/partials/w3m-header/index.tsx +++ b/packages/scaffold/src/partials/w3m-header/index.tsx @@ -18,23 +18,30 @@ export function Header() { const networkName = data?.network?.name; return { - Connect: 'Connect wallet', Account: undefined, - ConnectingWalletConnect: walletName ?? 'WalletConnect', + AccountDefault: undefined, + AllWallets: 'All wallets', + Connect: 'Connect wallet', ConnectingExternal: connectorName ?? 'Connect wallet', ConnectingSiwe: 'Sign In', - Networks: 'Select network', - SwitchNetwork: networkName ?? 'Switch network', - AllWallets: 'All wallets', - WhatIsANetwork: 'What is a network?', - WhatIsAWallet: 'What is a wallet?', - GetWallet: 'Get a wallet', + ConnectingWalletConnect: walletName ?? 'WalletConnect', EmailVerifyDevice: ' ', EmailVerifyOtp: 'Confirm email', - UpdateEmailWallet: 'Edit email', + GetWallet: 'Get a wallet', + Networks: 'Select network', + SwitchNetwork: networkName ?? 'Switch network', + Transactions: 'Activity', UpdateEmailPrimaryOtp: 'Confirm current email', UpdateEmailSecondaryOtp: 'Confirm new email', - UpgradeEmailWallet: 'Upgrade wallet' + UpdateEmailWallet: 'Edit email', + UpgradeEmailWallet: 'Upgrade wallet', + WalletCompatibleNetworks: 'Compatible networks', + WalletReceive: 'Receive', + WalletSend: 'Send', + WalletSendPreview: 'Review send', + WalletSendSelectToken: 'Select token', + WhatIsANetwork: 'What is a network?', + WhatIsAWallet: 'What is a wallet?' }; }; diff --git a/packages/scaffold/src/partials/w3m-input-address/index.tsx b/packages/scaffold/src/partials/w3m-input-address/index.tsx new file mode 100644 index 00000000..40e67a8a --- /dev/null +++ b/packages/scaffold/src/partials/w3m-input-address/index.tsx @@ -0,0 +1,72 @@ +import { useState } from 'react'; +import { TextInput } from 'react-native'; +import { FlexView, useTheme } from '@reown/appkit-ui-react-native'; +import { ConnectionController, SendController } from '@reown/appkit-core-react-native'; + +import { useDebounceCallback } from '../../hooks/useDebounceCallback'; +import styles from './styles'; + +export interface InputAddressProps { + value?: string; +} + +export function InputAddress({ value }: InputAddressProps) { + const Theme = useTheme(); + const [inputValue, setInputValue] = useState(value); + + const onSearch = async (search: string) => { + SendController.setLoading(true); + const address = await ConnectionController.getEnsAddress(search); + SendController.setLoading(false); + + if (address) { + SendController.setReceiverProfileName(search); + SendController.setReceiverAddress(address); + const avatar = await ConnectionController.getEnsAvatar(search); + SendController.setReceiverProfileImageUrl(avatar || undefined); + } else { + SendController.setReceiverAddress(search); + SendController.setReceiverProfileName(undefined); + SendController.setReceiverProfileImageUrl(undefined); + } + }; + + const onDebounceSearch = useDebounceCallback({ callback: onSearch, delay: 800 }); + + const onInputChange = (address: string) => { + setInputValue(address); + SendController.setReceiverAddress(address); + onDebounceSearch(address); + }; + + return ( + + + + ); +} diff --git a/packages/scaffold/src/partials/w3m-input-address/styles.ts b/packages/scaffold/src/partials/w3m-input-address/styles.ts new file mode 100644 index 00000000..bc205721 --- /dev/null +++ b/packages/scaffold/src/partials/w3m-input-address/styles.ts @@ -0,0 +1,14 @@ +import { StyleSheet } from 'react-native'; +import { BorderRadius } from '@reown/appkit-ui-react-native'; + +export default StyleSheet.create({ + container: { + height: 100, + width: '100%', + borderRadius: BorderRadius.s, + borderWidth: 1 + }, + input: { + fontSize: 18 + } +}); diff --git a/packages/scaffold/src/partials/w3m-input-token/intex.tsx b/packages/scaffold/src/partials/w3m-input-token/intex.tsx new file mode 100644 index 00000000..1e89612b --- /dev/null +++ b/packages/scaffold/src/partials/w3m-input-token/intex.tsx @@ -0,0 +1,113 @@ +import { useRef, useState } from 'react'; +import { TextInput, type StyleProp, type ViewStyle } from 'react-native'; +import { FlexView, Link, Text, useTheme, TokenButton } from '@reown/appkit-ui-react-native'; +import { NumberUtil, type Balance } from '@reown/appkit-common-react-native'; +import { ConstantsUtil, SendController } from '@reown/appkit-core-react-native'; + +import { getMaxAmount, getSendValue } from './utils'; +import styles from './styles'; + +export interface InputTokenProps { + token?: Balance; + sendTokenAmount?: number; + gasPrice?: number; + style?: StyleProp; + onTokenPress?: () => void; +} + +export function InputToken({ + token, + sendTokenAmount, + gasPrice, + style, + onTokenPress +}: InputTokenProps) { + const Theme = useTheme(); + const valueInputRef = useRef(null); + const [inputValue, setInputValue] = useState(sendTokenAmount?.toString()); + const sendValue = getSendValue(token, sendTokenAmount); + const maxAmount = getMaxAmount(token); + const maxError = token && sendTokenAmount && sendTokenAmount > Number(token.quantity.numeric); + + const onInputChange = (value: string) => { + const formattedValue = value.replace(/,/g, '.'); + setInputValue(formattedValue); + Number(formattedValue) + ? SendController.setTokenAmount(Number(formattedValue)) + : SendController.setTokenAmount(undefined); + }; + + const onMaxPress = () => { + if (token && gasPrice) { + const isNetworkToken = + token.address === undefined || + Object.values(ConstantsUtil.NATIVE_TOKEN_ADDRESS).some( + nativeAddress => token?.address === nativeAddress + ); + + const numericGas = NumberUtil.bigNumber(gasPrice).shiftedBy(-token.quantity.decimals); + + const maxValue = isNetworkToken + ? NumberUtil.bigNumber(token.quantity.numeric).minus(numericGas) + : NumberUtil.bigNumber(token.quantity.numeric); + + SendController.setTokenAmount(Number(maxValue.toFixed(20))); + setInputValue(maxValue.toFixed(20)); + valueInputRef.current?.blur(); + } + }; + + return ( + + + + + + + + {sendValue ?? ''} + + {token && ( + + + {maxAmount ?? ''} + + Max + + )} + + + ); +} diff --git a/packages/scaffold/src/partials/w3m-input-token/styles.ts b/packages/scaffold/src/partials/w3m-input-token/styles.ts new file mode 100644 index 00000000..610ea164 --- /dev/null +++ b/packages/scaffold/src/partials/w3m-input-token/styles.ts @@ -0,0 +1,20 @@ +import { BorderRadius, Spacing } from '@reown/appkit-ui-react-native'; +import { StyleSheet } from 'react-native'; + +export default StyleSheet.create({ + container: { + height: 100, + width: '100%', + borderRadius: BorderRadius.s, + borderWidth: 1 + }, + input: { + fontSize: 32, + flex: 1, + marginRight: Spacing.xs + }, + sendValue: { + flex: 1, + marginRight: Spacing.xs + } +}); diff --git a/packages/scaffold/src/partials/w3m-input-token/utils.ts b/packages/scaffold/src/partials/w3m-input-token/utils.ts new file mode 100644 index 00000000..38085ed3 --- /dev/null +++ b/packages/scaffold/src/partials/w3m-input-token/utils.ts @@ -0,0 +1,21 @@ +import { type Balance, NumberUtil } from '@reown/appkit-common-react-native'; +import { UiUtil } from '@reown/appkit-ui-react-native'; + +export function getSendValue(token?: Balance, sendTokenAmount?: number) { + if (token && sendTokenAmount) { + const price = token.price; + const totalValue = price * sendTokenAmount; + + return totalValue ? `$${UiUtil.formatNumberToLocalString(totalValue, 2)}` : 'Incorrect value'; + } + + return null; +} + +export function getMaxAmount(token?: Balance) { + if (token) { + return NumberUtil.roundNumber(Number(token.quantity.numeric), 6, 5); + } + + return null; +} diff --git a/packages/scaffold/src/views/w3m-account-view/components/upgrade-wallet-button.tsx b/packages/scaffold/src/views/w3m-account-default-view/components/upgrade-wallet-button.tsx similarity index 100% rename from packages/scaffold/src/views/w3m-account-view/components/upgrade-wallet-button.tsx rename to packages/scaffold/src/views/w3m-account-default-view/components/upgrade-wallet-button.tsx diff --git a/packages/scaffold/src/views/w3m-account-default-view/index.tsx b/packages/scaffold/src/views/w3m-account-default-view/index.tsx new file mode 100644 index 00000000..7ea67c5f --- /dev/null +++ b/packages/scaffold/src/views/w3m-account-default-view/index.tsx @@ -0,0 +1,222 @@ +import { useSnapshot } from 'valtio'; +import { useState } from 'react'; +import { Linking, ScrollView } from 'react-native'; +import { + AccountController, + ApiController, + AssetUtil, + ConnectionController, + ConnectorController, + CoreHelperUtil, + EventsController, + ModalController, + NetworkController, + OptionsController, + RouterController, + SnackController, + type AppKitFrameProvider +} from '@reown/appkit-core-react-native'; +import { + Avatar, + Button, + FlexView, + IconLink, + Text, + UiUtil, + Spacing, + ListItem +} from '@reown/appkit-ui-react-native'; +import { useCustomDimensions } from '../../hooks/useCustomDimensions'; +import { UpgradeWalletButton } from './components/upgrade-wallet-button'; +import styles from './styles'; + +export function AccountDefaultView() { + const { address, profileName, profileImage, balance, balanceSymbol, addressExplorerUrl } = + useSnapshot(AccountController.state); + const [disconnecting, setDisconnecting] = useState(false); + const { caipNetwork } = useSnapshot(NetworkController.state); + const { connectedConnector } = useSnapshot(ConnectorController.state); + const { history } = useSnapshot(RouterController.state); + const networkImage = AssetUtil.getNetworkImage(caipNetwork); + const showCopy = OptionsController.isClipboardAvailable(); + const isAuth = connectedConnector === 'AUTH'; + const showBalance = balance && !isAuth; + const showExplorer = addressExplorerUrl && !isAuth; + const showBack = history.length > 1; + const { padding } = useCustomDimensions(); + + async function onDisconnect() { + try { + setDisconnecting(true); + await ConnectionController.disconnect(); + AccountController.setIsConnected(false); + ModalController.close(); + setDisconnecting(false); + EventsController.sendEvent({ + type: 'track', + event: 'DISCONNECT_SUCCESS' + }); + } catch (error) { + EventsController.sendEvent({ + type: 'track', + event: 'DISCONNECT_ERROR' + }); + } + } + + const getUserEmail = () => { + const provider = ConnectorController.getAuthConnector()?.provider as AppKitFrameProvider; + if (!provider) return ''; + + return provider.getEmail(); + }; + + const onExplorerPress = () => { + if (AccountController.state.addressExplorerUrl) { + Linking.openURL(AccountController.state.addressExplorerUrl); + } + }; + + const onCopyAddress = () => { + if (AccountController.state.profileName) { + OptionsController.copyToClipboard(AccountController.state.profileName); + SnackController.showSuccess('Name copied'); + } else if (AccountController.state.address) { + OptionsController.copyToClipboard( + AccountController.state.profileName ?? AccountController.state.address + ); + SnackController.showSuccess('Address copied'); + } + }; + + const onActivityPress = () => { + RouterController.push('Transactions'); + }; + + const onNetworkPress = () => { + RouterController.push('Networks'); + + EventsController.sendEvent({ + type: 'track', + event: 'CLICK_NETWORKS' + }); + }; + + const onUpgradePress = () => { + EventsController.sendEvent({ type: 'track', event: 'EMAIL_UPGRADE_FROM_MODAL' }); + RouterController.push('UpgradeEmailWallet'); + }; + + const onEmailPress = () => { + RouterController.push('UpdateEmailWallet', { email: getUserEmail() }); + }; + + return ( + <> + {showBack && ( + + )} + + + + + + + {profileName + ? UiUtil.getTruncateString({ + string: profileName, + charsStart: 20, + charsEnd: 0, + truncate: 'end' + }) + : UiUtil.getTruncateString({ + string: address ?? '', + charsStart: 4, + charsEnd: 6, + truncate: 'middle' + })} + + {showCopy && ( + + )} + + {showBalance && ( + + {CoreHelperUtil.formatBalance(balance, balanceSymbol)} + + )} + {showExplorer && ( + + )} + + {isAuth && ( + <> + + + {getUserEmail()} + + + )} + + + {caipNetwork?.name} + + + {!isAuth && ( + + Activity + + )} + + Disconnect + + + + + + ); +} diff --git a/packages/scaffold/src/views/w3m-account-default-view/styles.ts b/packages/scaffold/src/views/w3m-account-default-view/styles.ts new file mode 100644 index 00000000..1d0389f0 --- /dev/null +++ b/packages/scaffold/src/views/w3m-account-default-view/styles.ts @@ -0,0 +1,28 @@ +import { Spacing } from '@reown/appkit-ui-react-native'; +import { StyleSheet } from 'react-native'; + +export default StyleSheet.create({ + backIcon: { + alignSelf: 'flex-end', + position: 'absolute', + zIndex: 1, + top: Spacing.l, + left: Spacing.xl + }, + closeIcon: { + alignSelf: 'flex-end', + position: 'absolute', + zIndex: 1, + top: Spacing.l, + right: Spacing.xl + }, + copyButton: { + marginLeft: Spacing['4xs'] + }, + actionButton: { + marginBottom: Spacing.xs + }, + upgradeButton: { + marginBottom: Spacing.s + } +}); diff --git a/packages/scaffold/src/views/w3m-account-view/index.tsx b/packages/scaffold/src/views/w3m-account-view/index.tsx index edc7106f..079041a7 100644 --- a/packages/scaffold/src/views/w3m-account-view/index.tsx +++ b/packages/scaffold/src/views/w3m-account-view/index.tsx @@ -1,203 +1,87 @@ import { useSnapshot } from 'valtio'; -import { useState } from 'react'; -import { Linking, ScrollView } from 'react-native'; +import { useEffect } from 'react'; +import { ScrollView } from 'react-native'; +import { + AccountPill, + FlexView, + Icon, + IconLink, + NetworkButton, + useTheme +} from '@reown/appkit-ui-react-native'; import { AccountController, ApiController, AssetUtil, - ConnectionController, - ConnectorController, - CoreHelperUtil, - EventsController, ModalController, NetworkController, OptionsController, RouterController, - SnackController, - type AppKitFrameProvider + SendController, + SnackController } from '@reown/appkit-core-react-native'; -import { - Avatar, - Button, - FlexView, - IconLink, - Text, - UiUtil, - Spacing, - ListItem -} from '@reown/appkit-ui-react-native'; + import { useCustomDimensions } from '../../hooks/useCustomDimensions'; -import { UpgradeWalletButton } from './components/upgrade-wallet-button'; import styles from './styles'; +import { AccountWalletFeatures } from '../../partials/w3m-account-wallet-features'; export function AccountView() { - const { address, profileName, profileImage, balance, balanceSymbol } = useSnapshot( - AccountController.state - ); - const [disconnecting, setDisconnecting] = useState(false); - const { caipNetwork } = useSnapshot(NetworkController.state); - const { connectedConnector } = useSnapshot(ConnectorController.state); - const networkImage = AssetUtil.getNetworkImage(caipNetwork); - const showCopy = OptionsController.isClipboardAvailable(); - const isAuth = connectedConnector === 'AUTH'; + const Theme = useTheme(); const { padding } = useCustomDimensions(); + const { caipNetwork } = useSnapshot(NetworkController.state); + const { address, profileName, profileImage } = useSnapshot(AccountController.state); - async function onDisconnect() { - try { - setDisconnecting(true); - await ConnectionController.disconnect(); - AccountController.setIsConnected(false); - ModalController.close(); - setDisconnecting(false); - EventsController.sendEvent({ - type: 'track', - event: 'DISCONNECT_SUCCESS' - }); - } catch (error) { - EventsController.sendEvent({ - type: 'track', - event: 'DISCONNECT_ERROR' - }); - } - } - - const onExplorerPress = () => { - if (AccountController.state.addressExplorerUrl) { - Linking.openURL(AccountController.state.addressExplorerUrl); + const onCopyAddress = (value: string) => { + if (OptionsController.isClipboardAvailable() && value) { + OptionsController.copyToClipboard(value); + SnackController.showSuccess('Address copied'); } }; - const onCopyAddress = () => { - if (AccountController.state.address) { - OptionsController.copyToClipboard( - AccountController.state.profileName ?? AccountController.state.address - ); - SnackController.showSuccess('Address copied'); - } + const onProfilePress = () => { + RouterController.push('AccountDefault'); }; const onNetworkPress = () => { RouterController.push('Networks'); - - EventsController.sendEvent({ - type: 'track', - event: 'CLICK_NETWORKS' - }); }; - const onUpgradePress = () => { - EventsController.sendEvent({ type: 'track', event: 'EMAIL_UPGRADE_FROM_MODAL' }); - RouterController.push('UpgradeEmailWallet'); - }; - - const getUserEmail = () => { - const provider = ConnectorController.getAuthConnector()?.provider as AppKitFrameProvider; - if (!provider) return ''; - - return provider.getEmail(); - }; - - const addressExplorerTemplate = () => { - if (!AccountController.state.addressExplorerUrl) return null; - - return ( - - ); - }; + useEffect(() => { + AccountController.fetchTokenBalance(); + SendController.resetSend(); + }, []); return ( - <> + + + + - - - - - - {profileName - ? UiUtil.getTruncateString({ - string: profileName, - charsStart: 20, - charsEnd: 0, - truncate: 'end' - }) - : UiUtil.getTruncateString({ - string: address ?? '', - charsStart: 4, - charsEnd: 6, - truncate: 'middle' - })} - - {showCopy && ( - - )} - - {balance && ( - - {CoreHelperUtil.formatBalance(balance, balanceSymbol)} - - )} - {addressExplorerTemplate()} - - {isAuth && ( - <> - - - RouterController.push('UpdateEmailWallet', { email: getUserEmail() }) - } - chevron - testID="button-email" - > - {getUserEmail()} - - - )} - - - {caipNetwork?.name} - - - - Disconnect - - - - - + + + + + ); } diff --git a/packages/scaffold/src/views/w3m-account-view/styles.ts b/packages/scaffold/src/views/w3m-account-view/styles.ts index a154e7db..8ba7578f 100644 --- a/packages/scaffold/src/views/w3m-account-view/styles.ts +++ b/packages/scaffold/src/views/w3m-account-view/styles.ts @@ -1,7 +1,17 @@ import { Spacing } from '@reown/appkit-ui-react-native'; -import { StyleSheet } from 'react-native'; +import { Platform, StyleSheet } from 'react-native'; export default StyleSheet.create({ + contentContainer: { + paddingBottom: Platform.select({ ios: Spacing.s }) + }, + networkIcon: { + alignSelf: 'flex-start', + position: 'absolute', + zIndex: 1, + top: Spacing.l, + left: Spacing.l + }, closeIcon: { alignSelf: 'flex-end', position: 'absolute', @@ -9,13 +19,9 @@ export default StyleSheet.create({ top: Spacing.l, right: Spacing.xl }, - copyButton: { - marginLeft: Spacing['4xs'] - }, - networkButton: { - marginVertical: Spacing.xs - }, - upgradeButton: { - marginBottom: Spacing.s + accountPill: { + alignSelf: 'center', + marginBottom: Spacing.s, + marginHorizontal: Spacing.s } }); diff --git a/packages/scaffold/src/views/w3m-transactions-view/index.tsx b/packages/scaffold/src/views/w3m-transactions-view/index.tsx new file mode 100644 index 00000000..b2f66295 --- /dev/null +++ b/packages/scaffold/src/views/w3m-transactions-view/index.tsx @@ -0,0 +1,14 @@ +import { StyleSheet } from 'react-native'; +import { Spacing } from '@reown/appkit-ui-react-native'; +import { AccountActivity } from '../../partials/w3m-account-activity'; + +export function TransactionsView() { + return ; +} + +const styles = StyleSheet.create({ + container: { + paddingHorizontal: Spacing.l, + marginTop: Spacing.s + } +}); diff --git a/packages/scaffold/src/views/w3m-upgrade-email-wallet-view/index.tsx b/packages/scaffold/src/views/w3m-upgrade-email-wallet-view/index.tsx index be8f4fa6..425d9729 100644 --- a/packages/scaffold/src/views/w3m-upgrade-email-wallet-view/index.tsx +++ b/packages/scaffold/src/views/w3m-upgrade-email-wallet-view/index.tsx @@ -1,5 +1,5 @@ import { useSnapshot } from 'valtio'; -import { StyleSheet } from 'react-native'; +import { Linking, StyleSheet } from 'react-native'; import { Chip, FlexView, Spacing, Text } from '@reown/appkit-ui-react-native'; import { ConnectorController, type AppKitFrameProvider } from '@reown/appkit-core-react-native'; @@ -7,6 +7,13 @@ export function UpgradeEmailWalletView() { const { connectors } = useSnapshot(ConnectorController.state); const authProvider = connectors.find(c => c.type === 'AUTH')?.provider as AppKitFrameProvider; + const onLinkPress = () => { + const link = authProvider.getSecureSiteDashboardURL(); + Linking.canOpenURL(link).then(supported => { + if (supported) Linking.openURL(link); + }); + }; + return ( Follow the instructions on @@ -14,8 +21,8 @@ export function UpgradeEmailWalletView() { label="secure.walletconnect.com" icon="externalLink" imageSrc={authProvider.getSecureSiteIconURL()} - link={authProvider.getSecureSiteDashboardURL()} style={styles.chip} + onPress={onLinkPress} /> You will have to reconnect for security reasons diff --git a/packages/scaffold/src/views/w3m-wallet-compatible-networks-view/index.tsx b/packages/scaffold/src/views/w3m-wallet-compatible-networks-view/index.tsx new file mode 100644 index 00000000..0c03c449 --- /dev/null +++ b/packages/scaffold/src/views/w3m-wallet-compatible-networks-view/index.tsx @@ -0,0 +1,37 @@ +import { ScrollView } from 'react-native'; +import { FlexView, Text, Banner, NetworkImage } from '@reown/appkit-ui-react-native'; +import { ApiController, AssetUtil, NetworkController } from '@reown/appkit-core-react-native'; +import { useCustomDimensions } from '../../hooks/useCustomDimensions'; +import styles from './styles'; + +export function WalletCompatibleNetworks() { + const { padding } = useCustomDimensions(); + const approvedNetworks = NetworkController.getApprovedCaipNetworks(); + const imageHeaders = ApiController._getApiHeaders(); + + return ( + + + + {approvedNetworks.map(network => ( + + + + {network.name} + + + ))} + + + ); +} diff --git a/packages/scaffold/src/views/w3m-wallet-compatible-networks-view/styles.ts b/packages/scaffold/src/views/w3m-wallet-compatible-networks-view/styles.ts new file mode 100644 index 00000000..de669ca6 --- /dev/null +++ b/packages/scaffold/src/views/w3m-wallet-compatible-networks-view/styles.ts @@ -0,0 +1,8 @@ +import { Spacing } from '@reown/appkit-ui-react-native'; +import { StyleSheet } from 'react-native'; + +export default StyleSheet.create({ + image: { + marginRight: Spacing.s + } +}); diff --git a/packages/scaffold/src/views/w3m-wallet-receive-view/index.tsx b/packages/scaffold/src/views/w3m-wallet-receive-view/index.tsx new file mode 100644 index 00000000..51693a34 --- /dev/null +++ b/packages/scaffold/src/views/w3m-wallet-receive-view/index.tsx @@ -0,0 +1,88 @@ +import { useSnapshot } from 'valtio'; +import { ScrollView, StyleSheet } from 'react-native'; +import { + Chip, + CompatibleNetwork, + FlexView, + QrCode, + Spacing, + Text, + UiUtil +} from '@reown/appkit-ui-react-native'; +import { + AccountController, + ApiController, + AssetUtil, + NetworkController, + OptionsController, + RouterController, + SnackController +} from '@reown/appkit-core-react-native'; +import { useCustomDimensions } from '../../hooks/useCustomDimensions'; + +export function WalletReceiveView() { + const { address, profileName } = useSnapshot(AccountController.state); + const { caipNetwork } = useSnapshot(NetworkController.state); + const networkImage = AssetUtil.getNetworkImage(caipNetwork); + const { padding } = useCustomDimensions(); + const canCopy = OptionsController.isClipboardAvailable(); + const slicedNetworks = + NetworkController.getApprovedCaipNetworks() + .filter(network => network?.imageId) + ?.slice(0, 5) || []; + const imagesArray = slicedNetworks.map(AssetUtil.getNetworkImage).filter(Boolean) as string[]; + + const label = UiUtil.getTruncateString({ + string: profileName ?? address ?? '', + charsStart: profileName ? 30 : 4, + charsEnd: profileName ? 0 : 4, + truncate: profileName ? 'end' : 'middle' + }); + + const onNetworkPress = () => { + RouterController.push('WalletCompatibleNetworks'); + }; + + const onCopyAddress = () => { + if (canCopy && AccountController.state.address) { + OptionsController.copyToClipboard(AccountController.state.address); + SnackController.showSuccess('Address copied'); + } + }; + + if (!address) return; + + return ( + + + + + + {canCopy ? 'Copy your address or scan this QR code' : 'Scan this QR code'} + + + + + ); +} + +const styles = StyleSheet.create({ + qrContainer: { + marginVertical: Spacing.xl + }, + networksButton: { + marginTop: Spacing.l + } +}); diff --git a/packages/scaffold/src/views/w3m-wallet-receive-view/styles.ts b/packages/scaffold/src/views/w3m-wallet-receive-view/styles.ts new file mode 100644 index 00000000..c866ab3d --- /dev/null +++ b/packages/scaffold/src/views/w3m-wallet-receive-view/styles.ts @@ -0,0 +1,8 @@ +import { StyleSheet } from 'react-native'; + +export default StyleSheet.create({ + container: { + justifyContent: 'center', + alignItems: 'center' + } +}); diff --git a/packages/scaffold/src/views/w3m-wallet-send-preview-view/components/preview-send-details.tsx b/packages/scaffold/src/views/w3m-wallet-send-preview-view/components/preview-send-details.tsx new file mode 100644 index 00000000..d71df174 --- /dev/null +++ b/packages/scaffold/src/views/w3m-wallet-send-preview-view/components/preview-send-details.tsx @@ -0,0 +1,101 @@ +import { AssetUtil, type CaipNetwork } from '@reown/appkit-core-react-native'; +import { + BorderRadius, + FlexView, + NetworkImage, + Spacing, + Text, + UiUtil, + useTheme +} from '@reown/appkit-ui-react-native'; +import { StyleSheet, type StyleProp, type ViewStyle } from 'react-native'; + +export interface PreviewSendDetailsProps { + address?: string; + name?: string; + caipNetwork?: CaipNetwork; + networkFee?: number; + style?: StyleProp; +} + +export function PreviewSendDetails({ + address, + name, + caipNetwork, + networkFee, + style +}: PreviewSendDetailsProps) { + const Theme = useTheme(); + + const formattedName = UiUtil.getTruncateString({ + string: name ?? '', + charsStart: 20, + charsEnd: 0, + truncate: 'end' + }); + + const formattedAddress = UiUtil.getTruncateString({ + string: address || '', + charsStart: 6, + charsEnd: 8, + truncate: 'middle' + }); + + const networkImage = AssetUtil.getNetworkImage(caipNetwork); + + return ( + + + Details + + + + Network cost + + + ${UiUtil.formatNumberToLocalString(networkFee, 2)} + + + + + {formattedName || 'Address'} + + + {formattedAddress} + + + + + Network + + + + + ); +} + +const styles = StyleSheet.create({ + container: { + justifyContent: 'center', + borderRadius: BorderRadius.xxs + }, + title: { + marginBottom: Spacing.xs + }, + item: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + padding: Spacing.s, + borderRadius: BorderRadius.xxs, + marginTop: Spacing['2xs'] + }, + networkImage: { + height: 24, + width: 24, + borderRadius: BorderRadius.full + } +}); diff --git a/packages/scaffold/src/views/w3m-wallet-send-preview-view/components/preview-send-pill.tsx b/packages/scaffold/src/views/w3m-wallet-send-preview-view/components/preview-send-pill.tsx new file mode 100644 index 00000000..b3c47700 --- /dev/null +++ b/packages/scaffold/src/views/w3m-wallet-send-preview-view/components/preview-send-pill.tsx @@ -0,0 +1,36 @@ +import { BorderRadius, FlexView, Text, useTheme } from '@reown/appkit-ui-react-native'; +import { StyleSheet } from 'react-native'; + +export interface PreviewSendPillProps { + text: string; + children: React.ReactNode; +} + +export function PreviewSendPill({ text, children }: PreviewSendPillProps) { + const Theme = useTheme(); + + return ( + + + {text} + + {children} + + ); +} + +const styles = StyleSheet.create({ + pill: { + borderRadius: BorderRadius.full, + borderWidth: 1 + } +}); diff --git a/packages/scaffold/src/views/w3m-wallet-send-preview-view/index.tsx b/packages/scaffold/src/views/w3m-wallet-send-preview-view/index.tsx new file mode 100644 index 00000000..8b9e7f41 --- /dev/null +++ b/packages/scaffold/src/views/w3m-wallet-send-preview-view/index.tsx @@ -0,0 +1,134 @@ +import { useSnapshot } from 'valtio'; +import { ScrollView } from 'react-native'; +import { Avatar, Button, FlexView, Icon, Image, Text, UiUtil } from '@reown/appkit-ui-react-native'; +import { NumberUtil } from '@reown/appkit-common-react-native'; +import { + NetworkController, + RouterController, + SendController +} from '@reown/appkit-core-react-native'; +import { useCustomDimensions } from '../../hooks/useCustomDimensions'; +import { PreviewSendPill } from './components/preview-send-pill'; +import styles from './styles'; +import { PreviewSendDetails } from './components/preview-send-details'; + +export function WalletSendPreviewView() { + const { padding } = useCustomDimensions(); + const { caipNetwork } = useSnapshot(NetworkController.state); + const { + token, + receiverAddress, + receiverProfileName, + receiverProfileImageUrl, + gasPriceInUSD, + loading + } = useSnapshot(SendController.state); + + const getSendValue = () => { + if (SendController.state.token && SendController.state.sendTokenAmount) { + const price = SendController.state.token.price; + const totalValue = price * SendController.state.sendTokenAmount; + + return totalValue.toFixed(2); + } + + return null; + }; + + const getTokenAmount = () => { + const value = SendController.state.sendTokenAmount + ? NumberUtil.roundNumber(SendController.state.sendTokenAmount, 6, 5) + : 'unknown'; + + return `${value} ${SendController.state.token?.symbol}`; + }; + + const formattedAddress = receiverProfileName + ? UiUtil.getTruncateString({ + string: receiverProfileName, + charsStart: 20, + charsEnd: 0, + truncate: 'end' + }) + : UiUtil.getTruncateString({ + string: receiverAddress || '', + charsStart: 4, + charsEnd: 4, + truncate: 'middle' + }); + + const onSend = () => { + SendController.sendToken(); + }; + + const onCancel = () => { + RouterController.goBack(); + SendController.setLoading(false); + }; + + return ( + + + + + + Send + + + ${getSendValue()} + + + + {token?.iconUrl ? ( + + ) : ( + + )} + + + + + + To + + + + + + + + + + Review transaction carefully + + + + + + + + + ); +} diff --git a/packages/scaffold/src/views/w3m-wallet-send-preview-view/styles.ts b/packages/scaffold/src/views/w3m-wallet-send-preview-view/styles.ts new file mode 100644 index 00000000..432a72c3 --- /dev/null +++ b/packages/scaffold/src/views/w3m-wallet-send-preview-view/styles.ts @@ -0,0 +1,35 @@ +import { BorderRadius, Spacing } from '@reown/appkit-ui-react-native'; +import { StyleSheet } from 'react-native'; + +export default StyleSheet.create({ + container: { + justifyContent: 'center', + alignItems: 'center' + }, + tokenLogo: { + height: 32, + width: 32, + borderRadius: BorderRadius.full, + marginLeft: Spacing.xs + }, + arrow: { + marginVertical: Spacing.xs + }, + avatar: { + marginLeft: Spacing.xs + }, + details: { + marginTop: Spacing['2xl'], + marginBottom: Spacing.s + }, + reviewIcon: { + marginRight: Spacing['3xs'] + }, + cancelButton: { + flex: 1 + }, + sendButton: { + marginLeft: Spacing.s, + flex: 3 + } +}); diff --git a/packages/scaffold/src/views/w3m-wallet-send-select-token-view/index.tsx b/packages/scaffold/src/views/w3m-wallet-send-select-token-view/index.tsx new file mode 100644 index 00000000..80175219 --- /dev/null +++ b/packages/scaffold/src/views/w3m-wallet-send-select-token-view/index.tsx @@ -0,0 +1,72 @@ +import { useState } from 'react'; +import { useSnapshot } from 'valtio'; +import { ScrollView } from 'react-native'; +import { FlexView, InputText, ListToken, Text } from '@reown/appkit-ui-react-native'; +import { + AccountController, + AssetUtil, + NetworkController, + RouterController, + SendController +} from '@reown/appkit-core-react-native'; +import type { Balance } from '@reown/appkit-common-react-native'; + +import { useCustomDimensions } from '../../hooks/useCustomDimensions'; +import styles from './styles'; + +export function WalletSendSelectTokenView() { + const { padding } = useCustomDimensions(); + const { tokenBalance } = useSnapshot(AccountController.state); + const { caipNetwork } = useSnapshot(NetworkController.state); + const networkImage = AssetUtil.getNetworkImage(caipNetwork); + const [tokenSearch, setTokenSearch] = useState(''); + const [filteredTokens, setFilteredTokens] = useState(tokenBalance ?? []); + + const onSearchChange = (value: string) => { + setTokenSearch(value); + const filtered = AccountController.state.tokenBalance?.filter(token => + token.name.toLowerCase().includes(value.toLowerCase()) + ); + setFilteredTokens(filtered ?? []); + }; + + const onTokenPress = (token: Balance) => { + SendController.setToken(token); + SendController.setTokenAmount(undefined); + RouterController.goBack(); + }; + + return ( + + + + + + + Your tokens + + {filteredTokens.map((token, index) => ( + onTokenPress(token)} + /> + ))} + + + ); +} diff --git a/packages/scaffold/src/views/w3m-wallet-send-select-token-view/styles.ts b/packages/scaffold/src/views/w3m-wallet-send-select-token-view/styles.ts new file mode 100644 index 00000000..23c2e7c5 --- /dev/null +++ b/packages/scaffold/src/views/w3m-wallet-send-select-token-view/styles.ts @@ -0,0 +1,15 @@ +import { StyleSheet } from 'react-native'; +import { Spacing } from '@reown/appkit-ui-react-native'; + +export default StyleSheet.create({ + container: { + minHeight: 250, + maxHeight: 600 + }, + title: { + marginBottom: Spacing.xs + }, + tokenList: { + paddingHorizontal: Spacing.m + } +}); diff --git a/packages/scaffold/src/views/w3m-wallet-send-view/index.tsx b/packages/scaffold/src/views/w3m-wallet-send-view/index.tsx new file mode 100644 index 00000000..8be4dd21 --- /dev/null +++ b/packages/scaffold/src/views/w3m-wallet-send-view/index.tsx @@ -0,0 +1,141 @@ +import { useCallback, useEffect } from 'react'; +import { Platform, ScrollView } from 'react-native'; +import { useSnapshot } from 'valtio'; +import { + AccountController, + CoreHelperUtil, + RouterController, + SendController, + SwapController +} from '@reown/appkit-core-react-native'; +import { + Button, + FlexView, + IconBox, + LoadingSpinner, + Spacing, + Text +} from '@reown/appkit-ui-react-native'; +import { InputToken } from '../../partials/w3m-input-token/intex'; +import { useCustomDimensions } from '../../hooks/useCustomDimensions'; +import { useKeyboard } from '../../hooks/useKeyboard'; +import { InputAddress } from '../../partials/w3m-input-address'; +import styles from './styles'; + +export function WalletSendView() { + const { padding } = useCustomDimensions(); + const { keyboardShown, keyboardHeight } = useKeyboard(); + const { token, sendTokenAmount, receiverAddress, receiverProfileName, loading, gasPrice } = + useSnapshot(SendController.state); + const { tokenBalance } = useSnapshot(AccountController.state); + + const paddingBottom = Platform.select({ + android: keyboardShown ? keyboardHeight + Spacing['2xl'] : Spacing['2xl'], + default: Spacing['2xl'] + }); + + const fetchNetworkPrice = useCallback(async () => { + await SwapController.getNetworkTokenPrice(); + const gas = await SwapController.getInitialGasPrice(); + if (gas?.gasPrice && gas?.gasPriceInUSD) { + SendController.setGasPrice(gas.gasPrice); + SendController.setGasPriceInUsd(gas.gasPriceInUSD); + } + }, []); + + const onSendPress = () => { + RouterController.push('WalletSendPreview'); + }; + + const getActionText = () => { + if (!SendController.state.token) { + return 'Select token'; + } + + if ( + SendController.state.sendTokenAmount && + SendController.state.token && + SendController.state.sendTokenAmount > Number(SendController.state.token.quantity.numeric) + ) { + return 'Insufficient funds'; + } + + if (!SendController.state.sendTokenAmount) { + return 'Add amount'; + } + + if (SendController.state.sendTokenAmount && SendController.state.token?.price) { + const value = SendController.state.sendTokenAmount * SendController.state.token.price; + if (!value) { + return 'Incorrect value'; + } + } + + if ( + SendController.state.receiverAddress && + !CoreHelperUtil.isAddress(SendController.state.receiverAddress) + ) { + return 'Invalid address'; + } + + if (!SendController.state.receiverAddress) { + return 'Add address'; + } + + return 'Preview send'; + }; + + useEffect(() => { + if (!token) { + SendController.setToken(tokenBalance?.[0]); + } + fetchNetworkPrice(); + }, [token, tokenBalance, fetchNetworkPrice]); + + const actionText = getActionText(); + + return ( + + + RouterController.push('WalletSendSelectToken')} + /> + + + + + + + + ); +} diff --git a/packages/scaffold/src/views/w3m-wallet-send-view/styles.ts b/packages/scaffold/src/views/w3m-wallet-send-view/styles.ts new file mode 100644 index 00000000..7fded219 --- /dev/null +++ b/packages/scaffold/src/views/w3m-wallet-send-view/styles.ts @@ -0,0 +1,27 @@ +import { BorderRadius, Spacing } from '@reown/appkit-ui-react-native'; +import { StyleSheet } from 'react-native'; + +export default StyleSheet.create({ + sendButton: { + width: '100%', + marginTop: Spacing.xl, + borderRadius: BorderRadius.xs + }, + tokenInput: { + marginBottom: Spacing.xs + }, + mockInput: { + width: '100%', + borderWidth: 1, + height: 120, + borderRadius: 20 + }, + arrowIcon: { + position: 'absolute', + top: -30, + borderRadius: 20 + }, + addressContainer: { + width: '100%' + } +}); diff --git a/packages/siwe/src/scaffold/partials/w3m-connecting-siwe/index.tsx b/packages/siwe/src/scaffold/partials/w3m-connecting-siwe/index.tsx index 27581c98..f53f5fcf 100644 --- a/packages/siwe/src/scaffold/partials/w3m-connecting-siwe/index.tsx +++ b/packages/siwe/src/scaffold/partials/w3m-connecting-siwe/index.tsx @@ -25,7 +25,7 @@ export function ConnectingSiwe({ style }: Props) { const Theme = useTheme(); const { metadata } = useSnapshot(OptionsController.state); const { connectedWalletImageUrl, pressedWallet } = useSnapshot(ConnectionController.state); - const { address, profileName, profileImage } = useSnapshot(AccountController.state); + const { address, profileImage } = useSnapshot(AccountController.state); const dappIcon = metadata?.icons[0] || ''; const dappPosition = useAnimatedValue(10); const walletPosition = useAnimatedValue(-10); @@ -106,12 +106,7 @@ export function ConnectingSiwe({ style }: Props) { {walletIcon ? ( ) : ( - + )} diff --git a/packages/siwe/src/scaffold/partials/w3m-connecting-siwe/styles.ts b/packages/siwe/src/scaffold/partials/w3m-connecting-siwe/styles.ts index d7391e1d..b7c00f05 100644 --- a/packages/siwe/src/scaffold/partials/w3m-connecting-siwe/styles.ts +++ b/packages/siwe/src/scaffold/partials/w3m-connecting-siwe/styles.ts @@ -1,10 +1,11 @@ +import { BorderRadius } from '@reown/appkit-ui-react-native'; import { StyleSheet } from 'react-native'; export default StyleSheet.create({ dappIcon: { height: 64, width: 64, - borderRadius: 100 + borderRadius: BorderRadius.full }, iconBorder: { width: 74, @@ -13,7 +14,7 @@ export default StyleSheet.create({ justifyContent: 'center' }, dappBorder: { - borderRadius: 100, + borderRadius: BorderRadius.full, zIndex: 2 }, walletBorder: { @@ -22,6 +23,6 @@ export default StyleSheet.create({ height: 72 }, walletAvatar: { - borderRadius: 100 + borderRadius: BorderRadius.full } }); diff --git a/packages/ui/src/assets/svg/ArrowBottomCircle.tsx b/packages/ui/src/assets/svg/ArrowBottomCircle.tsx new file mode 100644 index 00000000..4f19838a --- /dev/null +++ b/packages/ui/src/assets/svg/ArrowBottomCircle.tsx @@ -0,0 +1,12 @@ +import Svg, { Path, type SvgProps } from 'react-native-svg'; +const SvgArrowBottomCircle = (props: SvgProps) => ( + + + +); +export default SvgArrowBottomCircle; diff --git a/packages/ui/src/assets/svg/Paperplane.tsx b/packages/ui/src/assets/svg/Paperplane.tsx new file mode 100644 index 00000000..131e45f2 --- /dev/null +++ b/packages/ui/src/assets/svg/Paperplane.tsx @@ -0,0 +1,12 @@ +import Svg, { Path, type SvgProps } from 'react-native-svg'; +const SvgPaperplane = (props: SvgProps) => ( + + + +); +export default SvgPaperplane; diff --git a/packages/ui/src/components/wui-icon/index.tsx b/packages/ui/src/components/wui-icon/index.tsx index d34d7c9e..f587e862 100644 --- a/packages/ui/src/components/wui-icon/index.tsx +++ b/packages/ui/src/components/wui-icon/index.tsx @@ -5,6 +5,7 @@ import type { ColorType, IconType, SizeType, ThemeKeys } from '../../utils/Types import AllWalletsSvg from '../../assets/svg/AllWallets'; import AppleSvg from '../../assets/svg/Apple'; import ArrowBottomSvg from '../../assets/svg/ArrowBottom'; +import ArrowBottomCircleSvg from '../../assets/svg/ArrowBottomCircle'; import ArrowLeftSvg from '../../assets/svg/ArrowLeft'; import ArrowRightSvg from '../../assets/svg/ArrowRight'; import ArrowTopSvg from '../../assets/svg/ArrowTop'; @@ -39,6 +40,7 @@ import MobileSvg from '../../assets/svg/Mobile'; import NetworkPlaceholderSvg from '../../assets/svg/NetworkPlaceholder'; import NftPlaceholderSvg from '../../assets/svg/NftPlaceholder'; import OffSvg from '../../assets/svg/Off'; +import PaperplaneSvg from '../../assets/svg/Paperplane'; import QrCodeSvg from '../../assets/svg/QrCode'; import RefreshSvg from '../../assets/svg/Refresh'; import SearchSvg from '../../assets/svg/Search'; @@ -61,6 +63,7 @@ const svgOptions: Record JSX.Element> = { allWallets: AllWalletsSvg, apple: AppleSvg, arrowBottom: ArrowBottomSvg, + arrowBottomCircle: ArrowBottomCircleSvg, arrowLeft: ArrowLeftSvg, arrowRight: ArrowRightSvg, arrowTop: ArrowTopSvg, @@ -95,6 +98,7 @@ const svgOptions: Record JSX.Element> = { networkPlaceholder: NetworkPlaceholderSvg, nftPlaceholder: NftPlaceholderSvg, off: OffSvg, + paperplane: PaperplaneSvg, qrCode: QrCodeSvg, refresh: RefreshSvg, search: SearchSvg, diff --git a/packages/ui/src/composites/wui-account-button/index.tsx b/packages/ui/src/composites/wui-account-button/index.tsx index f77410a2..ccf9e4b9 100644 --- a/packages/ui/src/composites/wui-account-button/index.tsx +++ b/packages/ui/src/composites/wui-account-button/index.tsx @@ -15,7 +15,7 @@ export interface AccountButtonProps { imageHeaders?: Record; avatarSrc?: string; address?: string; - isProfileName?: boolean; + profileName?: string; balance?: string; onPress?: () => void; disabled?: boolean; @@ -28,7 +28,7 @@ export function AccountButton({ imageHeaders, avatarSrc, address, - isProfileName, + profileName, balance, onPress, disabled, @@ -73,6 +73,20 @@ export function AccountButton({ return null; } + const formattedAddress = profileName + ? UiUtil.getTruncateString({ + string: profileName, + charsStart: 18, + charsEnd: 0, + truncate: 'end' + }) + : UiUtil.getTruncateString({ + string: address || '', + charsStart: 4, + charsEnd: 6, + truncate: 'middle' + }); + return ( {address && ( - {UiUtil.getTruncateString({ - string: address, - charsStart: isProfileName ? 18 : 4, - charsEnd: isProfileName ? 0 : 6, - truncate: isProfileName ? 'end' : 'middle' - })} + {formattedAddress} )} diff --git a/packages/ui/src/composites/wui-account-button/styles.ts b/packages/ui/src/composites/wui-account-button/styles.ts index 61f2f0cf..1eda1c32 100644 --- a/packages/ui/src/composites/wui-account-button/styles.ts +++ b/packages/ui/src/composites/wui-account-button/styles.ts @@ -1,11 +1,11 @@ import { StyleSheet } from 'react-native'; -import { Spacing } from '../../utils/ThemeUtil'; +import { BorderRadius, Spacing } from '../../utils/ThemeUtil'; export default StyleSheet.create({ container: { flexDirection: 'row', height: 40, - borderRadius: 100, + borderRadius: BorderRadius.full, borderWidth: 1, justifyContent: 'center', alignItems: 'center', @@ -14,7 +14,7 @@ export default StyleSheet.create({ image: { height: 24, width: 24, - borderRadius: 100, + borderRadius: BorderRadius.full, borderWidth: 2 }, avatarPlaceholder: { @@ -35,7 +35,7 @@ export default StyleSheet.create({ alignItems: 'center', paddingLeft: Spacing['3xs'], paddingRight: Spacing.xs, - borderRadius: 100, + borderRadius: BorderRadius.full, borderWidth: 1 }, address: { diff --git a/packages/ui/src/composites/wui-account-pill/index.tsx b/packages/ui/src/composites/wui-account-pill/index.tsx new file mode 100644 index 00000000..9b7ebd88 --- /dev/null +++ b/packages/ui/src/composites/wui-account-pill/index.tsx @@ -0,0 +1,72 @@ +import { Animated, Pressable, type StyleProp, type ViewStyle } from 'react-native'; +import { Avatar } from '../wui-avatar'; +import { UiUtil } from '../../utils/UiUtil'; +import { IconLink } from '../wui-icon-link'; +import { Text } from '../../components/wui-text'; +import useAnimatedValue from '../../hooks/useAnimatedValue'; +import { useTheme } from '../../hooks/useTheme'; +import styles from './styles'; + +export interface AccountPillProps { + onPress: () => void; + onCopy: (address: string) => void; + address?: string; + profileName?: string; + profileImage?: string; + style?: StyleProp; +} + +const AnimatedPressable = Animated.createAnimatedComponent(Pressable); + +export function AccountPill({ + onPress, + onCopy, + address, + profileName, + profileImage, + style +}: AccountPillProps) { + const Theme = useTheme(); + + const { animatedValue, setStartValue, setEndValue } = useAnimatedValue( + Theme['gray-glass-005'], + Theme['gray-glass-010'] + ); + + const backgroundColor = animatedValue; + const borderColor = Theme['gray-glass-005']; + + const handleCopyAddress = () => { + if (address) { + onCopy(address); + } + }; + + return ( + + + + {profileName + ? UiUtil.getTruncateString({ + string: profileName, + charsStart: 17, + charsEnd: 0, + truncate: 'end' + }) + : UiUtil.getTruncateString({ + string: address ?? '', + charsStart: 4, + charsEnd: 4, + truncate: 'middle' + })} + + + + ); +} diff --git a/packages/ui/src/composites/wui-account-pill/styles.ts b/packages/ui/src/composites/wui-account-pill/styles.ts new file mode 100644 index 00000000..75d118b6 --- /dev/null +++ b/packages/ui/src/composites/wui-account-pill/styles.ts @@ -0,0 +1,19 @@ +import { StyleSheet } from 'react-native'; +import { BorderRadius, Spacing } from '../../utils/ThemeUtil'; + +export default StyleSheet.create({ + container: { + height: 44, + minWidth: 160, + maxWidth: 260, + paddingLeft: Spacing.xs, + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + borderRadius: BorderRadius.full, + borderWidth: 1 + }, + text: { + marginLeft: Spacing['2xs'] + } +}); diff --git a/packages/ui/src/composites/wui-avatar/styles.ts b/packages/ui/src/composites/wui-avatar/styles.ts index b16828ba..d3da925f 100644 --- a/packages/ui/src/composites/wui-avatar/styles.ts +++ b/packages/ui/src/composites/wui-avatar/styles.ts @@ -1,7 +1,8 @@ import { StyleSheet } from 'react-native'; +import { BorderRadius } from '../../utils/ThemeUtil'; export default StyleSheet.create({ image: { - borderRadius: 100 + borderRadius: BorderRadius.full } }); diff --git a/packages/ui/src/composites/wui-balance/index.tsx b/packages/ui/src/composites/wui-balance/index.tsx new file mode 100644 index 00000000..1f4a9a5e --- /dev/null +++ b/packages/ui/src/composites/wui-balance/index.tsx @@ -0,0 +1,25 @@ +import { StyleSheet } from 'react-native'; +import { Text } from '../../components/wui-text'; + +export interface BalanceProps { + integer?: string; + decimal?: string; +} + +export function Balance({ integer = '0', decimal = '00' }: BalanceProps) { + return ( + + {`$${integer}`} + + {`.${decimal}`} + + + ); +} + +const styles = StyleSheet.create({ + text: { + fontSize: 40, + fontWeight: '500' + } +}); diff --git a/packages/ui/src/composites/wui-banner/index.tsx b/packages/ui/src/composites/wui-banner/index.tsx new file mode 100644 index 00000000..dfeb02bd --- /dev/null +++ b/packages/ui/src/composites/wui-banner/index.tsx @@ -0,0 +1,28 @@ +import type { IconType } from '../../utils/TypesUtil'; +import { FlexView } from '../../layout/wui-flex'; +import { IconBox } from '../wui-icon-box'; +import { Text } from '../../components/wui-text'; +import { useTheme } from '../../hooks/useTheme'; +import styles from './styles'; + +export interface BannerProps { + icon: IconType; + text: string; +} + +export function Banner({ icon, text }: BannerProps) { + const Theme = useTheme(); + + return ( + + + + {text} + + + ); +} diff --git a/packages/ui/src/composites/wui-banner/styles.ts b/packages/ui/src/composites/wui-banner/styles.ts new file mode 100644 index 00000000..9504601b --- /dev/null +++ b/packages/ui/src/composites/wui-banner/styles.ts @@ -0,0 +1,15 @@ +import { StyleSheet } from 'react-native'; +import { BorderRadius, Spacing } from '../../utils/ThemeUtil'; + +export default StyleSheet.create({ + container: { + padding: Spacing.s, + borderRadius: BorderRadius.s + }, + icon: { + marginRight: Spacing.xs + }, + text: { + flex: 1 + } +}); diff --git a/packages/ui/src/composites/wui-button/index.tsx b/packages/ui/src/composites/wui-button/index.tsx index 46645c16..697c05ca 100644 --- a/packages/ui/src/composites/wui-button/index.tsx +++ b/packages/ui/src/composites/wui-button/index.tsx @@ -95,16 +95,18 @@ export function Button({ style={[styles.iconLeft, iconStyle]} /> )} - {loading ? ( - - ) : ( - - {children} - - )} + {loading && } + {!loading && + (typeof children === 'string' ? ( + + {children} + + ) : ( + children + ))} {iconRight && ( ; disabled?: boolean; style?: StyleProp; + onPress?: () => void; } export function Chip({ - link, + onPress, imageSrc, icon, variant = 'fill', @@ -38,10 +38,8 @@ export function Chip({ const themedTextColor = getThemedTextColor(variant, disabled, pressed); const iconSize = size === 'md' ? 'sm' : 'xs'; - const onPress = () => { - Linking.canOpenURL(link).then(supported => { - if (supported) Linking.openURL(link); - }); + const handlePress = () => { + onPress?.(); }; const onPressIn = () => { @@ -78,7 +76,7 @@ export function Chip({ style={[styles.container, styles[`${size}Chip`], { borderColor, backgroundColor }, style]} onPressIn={onPressIn} onPressOut={onPressOut} - onPress={onPress} + onPress={handlePress} > {imageSrc && ( - {label || link} + {label} {icon && ( void; + networkImages: string[]; + imageHeaders: Record; + style?: StyleProp; +} + +const offset = [20, 15, 10, 5, 0]; +const zIndex = [5, 4, 3, 2, 1]; + +export function CompatibleNetwork({ + text, + onPress, + networkImages, + imageHeaders, + style +}: CompatibleNetworkProps) { + const Theme = useTheme(); + + return ( + + + {text} + + + {networkImages?.map((image, index) => ( + + ))} + + + ); +} + +const styles = StyleSheet.create({ + container: { + height: 48 + }, + contentContainer: { + flexDirection: 'row', + justifyContent: 'space-between', + paddingRight: 0 + } +}); diff --git a/packages/ui/src/composites/wui-list-item/index.tsx b/packages/ui/src/composites/wui-list-item/index.tsx index 98791b65..9cbacb17 100644 --- a/packages/ui/src/composites/wui-list-item/index.tsx +++ b/packages/ui/src/composites/wui-list-item/index.tsx @@ -5,7 +5,7 @@ import { Image } from '../../components/wui-image'; import { LoadingSpinner } from '../../components/wui-loading-spinner'; import useAnimatedValue from '../../hooks/useAnimatedValue'; import { useTheme } from '../../hooks/useTheme'; -import type { IconType } from '../../utils/TypesUtil'; +import type { ColorType, IconType } from '../../utils/TypesUtil'; import { IconBox } from '../wui-icon-box'; import styles from './styles'; @@ -13,8 +13,9 @@ const AnimatedPressable = Animated.createAnimatedComponent(Pressable); export interface ListItemProps { icon?: IconType; - iconVariant?: 'blue' | 'overlay'; - variant?: 'image' | 'icon'; + iconColor?: ColorType; + iconBackgroundColor?: ColorType; + iconBorderColor?: ColorType; imageSrc?: string; imageHeaders?: Record; chevron?: boolean; @@ -23,21 +24,24 @@ export interface ListItemProps { onPress?: () => void; children?: ReactNode; style?: StyleProp; + contentStyle?: StyleProp; testID?: string; } export function ListItem({ children, icon, - variant, imageSrc, imageHeaders, - iconVariant = 'blue', + iconColor = 'fg-200', + iconBackgroundColor, + iconBorderColor = 'gray-glass-005', chevron, loading, disabled, onPress, style, + contentStyle, testID }: ListItemProps) { const Theme = useTheme(); @@ -47,7 +51,7 @@ export function ListItem({ ); function visualTemplate() { - if (variant === 'image' && imageSrc) { + if (imageSrc) { return ( ); - } else if (variant === 'icon' && icon) { - const iconColor = iconVariant === 'blue' ? 'accent-100' : 'fg-200'; - const borderColor = iconVariant === 'blue' ? 'accent-glass-005' : 'gray-glass-005'; - + } else if (icon) { return ( - + ); @@ -81,7 +82,7 @@ export function ListItem({ if (loading) { return ; } else if (chevron) { - return ; + return ; } return null; @@ -101,7 +102,7 @@ export function ListItem({ testID={testID} > {visualTemplate()} - {children} + {children} {rightTemplate()} ); diff --git a/packages/ui/src/composites/wui-list-item/styles.ts b/packages/ui/src/composites/wui-list-item/styles.ts index 4e12fe62..f7c9d79a 100644 --- a/packages/ui/src/composites/wui-list-item/styles.ts +++ b/packages/ui/src/composites/wui-list-item/styles.ts @@ -13,12 +13,13 @@ export default StyleSheet.create({ content: { flexDirection: 'row', flexGrow: 1, - paddingHorizontal: Spacing.s + paddingHorizontal: Spacing.s, + alignItems: 'center' }, imageContainer: { width: 36, height: 36, - borderRadius: 100, + borderRadius: BorderRadius.full, borderWidth: 2, alignItems: 'center', justifyContent: 'center' @@ -26,7 +27,7 @@ export default StyleSheet.create({ image: { width: 32, height: 32, - borderRadius: 100 + borderRadius: BorderRadius.full }, disabledImage: { opacity: 0.4 diff --git a/packages/ui/src/composites/wui-list-token/index.tsx b/packages/ui/src/composites/wui-list-token/index.tsx new file mode 100644 index 00000000..30b8662e --- /dev/null +++ b/packages/ui/src/composites/wui-list-token/index.tsx @@ -0,0 +1,83 @@ +import { Pressable } from 'react-native'; +import { Icon } from '../../components/wui-icon'; +import { Image } from '../../components/wui-image'; +import { Text } from '../../components/wui-text'; +import { useTheme } from '../../hooks/useTheme'; +import { FlexView } from '../../layout/wui-flex'; +import { UiUtil } from '../../utils/UiUtil'; +import styles from './styles'; + +export interface ListTokenProps { + imageSrc: string; + networkSrc?: string; + name: string; + value?: number; + amount?: string; + currency: string; + onPress?: () => void; +} + +export function ListToken({ + imageSrc, + networkSrc, + name, + value, + amount, + currency, + onPress +}: ListTokenProps) { + const Theme = useTheme(); + + return ( + + + + {imageSrc ? ( + + ) : ( + + + + )} + + {networkSrc ? ( + + ) : ( + + )} + + + + {name} + + + {UiUtil.formatNumberToLocalString(amount, 4)} {currency} + + + + + ${value?.toFixed(2) ?? '0.00'} + + + + ); +} diff --git a/packages/ui/src/composites/wui-list-token/styles.ts b/packages/ui/src/composites/wui-list-token/styles.ts new file mode 100644 index 00000000..73afea33 --- /dev/null +++ b/packages/ui/src/composites/wui-list-token/styles.ts @@ -0,0 +1,24 @@ +import { StyleSheet } from 'react-native'; +import { BorderRadius, WalletImageSize } from '../../utils/ThemeUtil'; + +export default StyleSheet.create({ + image: { + height: WalletImageSize.sm, + width: WalletImageSize.sm, + borderRadius: BorderRadius.full + }, + networkImageContainer: { + position: 'absolute', + bottom: -2, + left: 24, + borderWidth: 2, + borderRadius: BorderRadius.full, + width: 18, + height: 18 + }, + networkImage: { + width: 14, + height: 14, + borderRadius: BorderRadius.full + } +}); diff --git a/packages/ui/src/composites/wui-list-transaction/index.tsx b/packages/ui/src/composites/wui-list-transaction/index.tsx new file mode 100644 index 00000000..7cb10bb8 --- /dev/null +++ b/packages/ui/src/composites/wui-list-transaction/index.tsx @@ -0,0 +1,65 @@ +import { type TransactionImage, type TransactionStatus } from '@reown/appkit-common-react-native'; + +import type { TransactionType } from '../../utils/TypesUtil'; +import { Text } from '../../components/wui-text'; +import { FlexView } from '../../layout/wui-flex'; +import { IconBox } from '../wui-icon-box'; +import { TransactionVisual } from '../wui-transaction-visual'; +import { getIcon, getTypeLabel, getIconColor } from './utils'; +import styles from './styles'; +import type { StyleProp, ViewStyle } from 'react-native'; + +export interface ListTransactionProps { + date: string; + status?: TransactionStatus; + type?: TransactionType; + descriptions?: string[]; + images?: TransactionImage[]; + networkSrc?: string; + style?: StyleProp; + isAllNFT?: boolean; +} + +export function ListTransaction({ + date, + type, + descriptions, + images, + networkSrc, + style, + isAllNFT, + status +}: ListTransactionProps) { + const joinSymbol = type === 'trade' ? ' → ' : ' - '; + + return ( + + + + + + {type && ( + + )} + + {getTypeLabel(type)} + + + + {descriptions?.join(joinSymbol)} + + + + + {date} + + + ); +} diff --git a/packages/ui/src/composites/wui-list-transaction/styles.ts b/packages/ui/src/composites/wui-list-transaction/styles.ts new file mode 100644 index 00000000..5554145c --- /dev/null +++ b/packages/ui/src/composites/wui-list-transaction/styles.ts @@ -0,0 +1,10 @@ +import { StyleSheet } from 'react-native'; + +export default StyleSheet.create({ + middleContainer: { + flex: 1 + }, + date: { + textTransform: 'uppercase' + } +}); diff --git a/packages/ui/src/composites/wui-list-transaction/utils.ts b/packages/ui/src/composites/wui-list-transaction/utils.ts new file mode 100644 index 00000000..30558182 --- /dev/null +++ b/packages/ui/src/composites/wui-list-transaction/utils.ts @@ -0,0 +1,83 @@ +import type { TransactionStatus } from '@reown/appkit-common-react-native'; +import type { IconType, TransactionType } from '../../utils/TypesUtil'; + +export const getIcon = (type: TransactionType): IconType => { + switch (type) { + case 'approve': + case 'execute': + return 'checkmark'; + case 'repay': + case 'send': + case 'stake': + case 'withdraw': + return 'arrowTop'; + case 'burn': + case 'cancel': + return 'close'; + case 'trade': + return 'swapHorizontal'; + case 'deploy': + return 'arrowRight'; + default: + return 'arrowBottom'; + } +}; + +//Utils +export const getIconColor = (status?: TransactionStatus) => { + switch (status) { + case 'confirmed': + return 'success-100'; + case 'failed': + return 'error-100'; + case 'pending': + return 'fg-200'; + default: + return 'fg-200'; + } +}; + +export const getTypeLabel = (type?: TransactionType) => { + if (!type) { + return 'Unknown'; + } + + switch (type) { + case 'approve': + return 'Approved'; + case 'bought': + return 'Bought'; + case 'borrow': + return 'Borrowed'; + case 'burn': + return 'Burnt'; + case 'cancel': + return 'Canceled'; + case 'claim': + return 'Claimed'; + case 'deploy': + return 'Deployed'; + case 'deposit': + return 'Deposited'; + case 'execute': + return 'Executed'; + case 'mint': + return 'Minted'; + case 'receive': + return 'Received'; + case 'repay': + return 'Repaid'; + case 'send': + return 'Sent'; + case 'stake': + return 'Staked'; + case 'trade': + return 'Swapped'; + case 'unstake': + return 'Unstaked'; + case 'withdraw': + return 'Withdrawn'; + default: + return 'Unknown'; + } +}; diff --git a/packages/ui/src/composites/wui-network-button/index.tsx b/packages/ui/src/composites/wui-network-button/index.tsx index 11fe9484..b5c37a01 100644 --- a/packages/ui/src/composites/wui-network-button/index.tsx +++ b/packages/ui/src/composites/wui-network-button/index.tsx @@ -1,47 +1,50 @@ -import { Animated, Pressable, type StyleProp, type ViewStyle } from 'react-native'; +import { Animated, Pressable, View, type StyleProp, type ViewStyle } from 'react-native'; import { Image } from '../../components/wui-image'; import { Text } from '../../components/wui-text'; import { useTheme } from '../../hooks/useTheme'; import { IconBox } from '../wui-icon-box'; +import { LoadingSpinner } from '../../components/wui-loading-spinner'; +import useAnimatedValue from '../../hooks/useAnimatedValue'; import styles from './styles'; -import useAnimatedValue from '../../hooks/useAnimatedValue'; -import { LoadingSpinner } from '../../components/wui-loading-spinner'; const AnimatedPressable = Animated.createAnimatedComponent(Pressable); export interface NetworkButtonProps { - children: string; + children: string | React.ReactNode; onPress: () => void; + background?: boolean; + disabled?: boolean; imageSrc?: string; imageHeaders?: Record; - disabled?: boolean; - style?: StyleProp; loading?: boolean; + style?: StyleProp; } export function NetworkButton({ + children, + onPress, + background = true, + disabled, imageSrc, imageHeaders, - disabled, - onPress, - style, loading, - children + style }: NetworkButtonProps) { const Theme = useTheme(); const textColor = disabled ? 'fg-300' : 'fg-100'; const { animatedValue, setStartValue, setEndValue } = useAnimatedValue( - Theme['gray-glass-005'], + background ? Theme['gray-glass-005'] : 'transparent', Theme['gray-glass-010'] ); const backgroundColor = disabled ? Theme['gray-glass-015'] : animatedValue; + const borderColor = background ? Theme['gray-glass-005'] : 'transparent'; return ( - - {children} - + {typeof children === 'string' ? ( + + {children} + + ) : ( + {children} + )} ); } diff --git a/packages/ui/src/composites/wui-network-button/styles.ts b/packages/ui/src/composites/wui-network-button/styles.ts index eb36d018..199f2d29 100644 --- a/packages/ui/src/composites/wui-network-button/styles.ts +++ b/packages/ui/src/composites/wui-network-button/styles.ts @@ -1,5 +1,5 @@ import { StyleSheet } from 'react-native'; -import { Spacing } from '../../utils/ThemeUtil'; +import { BorderRadius, Spacing } from '../../utils/ThemeUtil'; export default StyleSheet.create({ container: { @@ -8,10 +8,10 @@ export default StyleSheet.create({ alignItems: 'center', justifyContent: 'center', borderWidth: 1, - borderRadius: 100, + borderRadius: BorderRadius.full, paddingHorizontal: Spacing['2xs'] }, - text: { + children: { paddingHorizontal: Spacing['2xs'] }, loader: { @@ -20,7 +20,7 @@ export default StyleSheet.create({ image: { height: 24, width: 24, - borderRadius: 100, + borderRadius: BorderRadius.full, borderWidth: 2, paddingLeft: Spacing['4xs'] }, diff --git a/packages/ui/src/composites/wui-network-image/index.tsx b/packages/ui/src/composites/wui-network-image/index.tsx index 4dd561ec..ee3ee9ac 100644 --- a/packages/ui/src/composites/wui-network-image/index.tsx +++ b/packages/ui/src/composites/wui-network-image/index.tsx @@ -1,49 +1,85 @@ import { Path, Svg, Image, Defs, Pattern } from 'react-native-svg'; +import type { StyleProp, ViewStyle } from 'react-native'; import { useTheme } from '../../hooks/useTheme'; import type { SizeType } from '../../utils/TypesUtil'; -import { PathLg, PathNormal } from './styles'; +import { Icon } from '../../components/wui-icon'; +import { FlexView } from '../../layout/wui-flex'; +import { PathLg, PathNormal, PathSmall, PathXS } from './styles'; export interface NetworkImageProps { imageSrc?: string; imageHeaders?: Record; selected?: boolean; - size?: Exclude; + size?: Exclude; disabled?: boolean; + style?: StyleProp; + borderColor?: string; + borderWidth?: number; } +const sizeToPath = { + lg: PathLg, + md: PathNormal, + sm: PathSmall, + xs: PathXS +}; + +const sizeToHeight = { + lg: 96, + md: 56, + sm: 40, + xs: 20 +}; + export function NetworkImage({ imageSrc, imageHeaders, disabled, selected, - size = 'md' + size = 'md', + style, + borderColor, + borderWidth = 1 }: NetworkImageProps) { const Theme = useTheme(); - const isLg = size === 'lg'; - const svgWidth = isLg ? 96 : 56; - const svgHeight = isLg ? 96 : 56; const svgStroke = selected ? Theme['accent-100'] : Theme['gray-glass-010']; const opacity = disabled ? 0.5 : 1; return ( - + - - + + {imageSrc ? ( + + ) : ( + + + + )} - + {!imageSrc && } + ); } diff --git a/packages/ui/src/composites/wui-network-image/styles.ts b/packages/ui/src/composites/wui-network-image/styles.ts index 7041b19c..dbd445ca 100644 --- a/packages/ui/src/composites/wui-network-image/styles.ts +++ b/packages/ui/src/composites/wui-network-image/styles.ts @@ -3,3 +3,9 @@ export const PathLg = export const PathNormal = 'M24.0002 2.34328C26.4754 0.914219 29.525 0.914219 32.0002 2.34328L48.2489 11.7245C50.7241 13.1535 52.2489 15.7946 52.2489 18.6527V37.4151C52.2489 40.2732 50.7241 42.9142 48.2489 44.3433L32.0002 53.7245C29.525 55.1535 26.4754 55.1535 24.0002 53.7245L7.75146 44.3433C5.27625 42.9142 3.75146 40.2732 3.75146 37.4151V18.6527C3.75146 15.7946 5.27626 13.1535 7.75146 11.7245L24.0002 2.34328Z'; + +export const PathSmall = + 'M17.1428 1.67377C18.9108 0.653013 21.0891 0.653014 22.8571 1.67377L34.4633 8.37463C36.2313 9.39539 37.3205 11.2818 37.3205 13.3233V26.7251C37.3205 28.7666 36.2313 30.653 34.4633 31.6738L22.8571 38.3746C21.0891 39.3954 18.9108 39.3954 17.1428 38.3746L5.53659 31.6738C3.76858 30.653 2.67944 28.7666 2.67944 26.7251V13.3233C2.67944 11.2818 3.76858 9.39539 5.53659 8.37463L17.1428 1.67377Z'; + +export const PathXS = + 'M8.57153 0.836886C9.45553 0.326507 10.5447 0.326507 11.4287 0.836886L17.2318 4.18731C18.1158 4.69769 18.6604 5.64091 18.6604 6.66167V13.3625C18.6604 14.3833 18.1158 15.3265 17.2318 15.8369L11.4287 19.1873C10.5447 19.6977 9.45553 19.6977 8.57153 19.1873L2.76841 15.8369C1.88441 15.3265 1.33984 14.3833 1.33984 13.3625V6.66167C1.33984 5.64091 1.88441 4.69769 2.76841 4.18731L8.57153 0.836886Z'; diff --git a/packages/ui/src/composites/wui-qr-code/index.tsx b/packages/ui/src/composites/wui-qr-code/index.tsx index 582a3ae2..f65ee57f 100644 --- a/packages/ui/src/composites/wui-qr-code/index.tsx +++ b/packages/ui/src/composites/wui-qr-code/index.tsx @@ -1,5 +1,5 @@ import { useMemo } from 'react'; -import { View } from 'react-native'; +import { View, type StyleProp, type ViewStyle } from 'react-native'; import Svg from 'react-native-svg'; import { Icon } from '../../components/wui-icon'; import { Image } from '../../components/wui-image'; @@ -15,22 +15,26 @@ export interface QrCodeProps { uri?: string; imageSrc?: string; testID?: string; + arenaClear?: boolean; + style?: StyleProp; } -export function QrCode({ size, uri, imageSrc, testID }: QrCodeProps) { +export function QrCode({ size, uri, imageSrc, testID, arenaClear, style }: QrCodeProps) { const Theme = LightTheme; const containerPadding = Spacing.l; const qrSize = size - containerPadding * 2; + const logoSize = arenaClear ? 0 : qrSize / 4; + const dots = useMemo( - () => (uri ? QRCodeUtil.generate(uri, qrSize, qrSize / 4) : []), - [uri, qrSize] + () => (uri ? QRCodeUtil.generate(uri, qrSize, logoSize) : []), + [uri, qrSize, logoSize] ); - const shimmerTemplate = () => { - return ; - }; - const logoTemplate = () => { + if (arenaClear) { + return null; + } + if (imageSrc) { return ( @@ -76,6 +81,6 @@ export function QrCode({ size, uri, imageSrc, testID }: QrCodeProps) { ) : ( - shimmerTemplate() + ); } diff --git a/packages/ui/src/composites/wui-snackbar/index.tsx b/packages/ui/src/composites/wui-snackbar/index.tsx index de00e654..299e79e5 100644 --- a/packages/ui/src/composites/wui-snackbar/index.tsx +++ b/packages/ui/src/composites/wui-snackbar/index.tsx @@ -17,6 +17,7 @@ export function Snackbar({ message, iconColor, icon, style }: SnackbarProps) { return ( void; - tabs: TabOptionType[]; + tabs: TabOptionType[] | string[]; + style?: StyleProp; } -export function Tabs({ tabs, onTabChange }: TabsProps) { +export function Tabs({ tabs, onTabChange, style }: TabsProps) { const Theme = useTheme(); const [activeTab, setActiveTab] = useState(0); const animatedPosition = useRef(new Animated.Value(0)); + const [viewWidth, setViewWidth] = useState(1); + const tabWidth = Math.trunc(viewWidth / tabs.length) - 2; const onTabPress = (index: number) => { setActiveTab(index); @@ -28,27 +38,41 @@ export function Tabs({ tabs, onTabChange }: TabsProps) { const markPosition = animatedPosition.current.interpolate({ inputRange: [0, tabs.length - 1], - outputRange: [0, 100 * (tabs.length - 1)] + outputRange: [0, tabWidth * (tabs.length - 1)] }); + const onLayout = (event: LayoutChangeEvent) => { + const { width } = event.nativeEvent.layout; + setViewWidth(width); + }; + return ( - + {tabs.map((option, index) => { const isActive = index === activeTab; + const isString = typeof option === 'string'; return ( - onTabPress(index)} key={option.label} style={styles.tabItem}> - {option.icon && ( + onTabPress(index)} + key={isString ? option : option.label} + style={[styles.tabItem, { width: tabWidth }]} + > + {!isString && option.icon && ( )} - {option.label} + {isString ? option : option.label} ); diff --git a/packages/ui/src/composites/wui-tabs/styles.ts b/packages/ui/src/composites/wui-tabs/styles.ts index 7d1dca5d..6a81b4fb 100644 --- a/packages/ui/src/composites/wui-tabs/styles.ts +++ b/packages/ui/src/composites/wui-tabs/styles.ts @@ -4,6 +4,7 @@ import { BorderRadius, Spacing } from '../../utils/ThemeUtil'; export default StyleSheet.create({ container: { height: 34, + width: '100%', flexDirection: 'row', alignItems: 'center', paddingHorizontal: Spacing['3xs'], @@ -13,7 +14,6 @@ export default StyleSheet.create({ flexDirection: 'row', justifyContent: 'center', alignItems: 'center', - width: 100, paddingVertical: Spacing['2xs'] }, tabIcon: { @@ -22,7 +22,6 @@ export default StyleSheet.create({ activeMark: { position: 'absolute', height: 28, - width: 100, borderWidth: 1, borderRadius: BorderRadius['3xl'], margin: Spacing['3xs'] diff --git a/packages/ui/src/composites/wui-token-button/index.tsx b/packages/ui/src/composites/wui-token-button/index.tsx new file mode 100644 index 00000000..b7a489e2 --- /dev/null +++ b/packages/ui/src/composites/wui-token-button/index.tsx @@ -0,0 +1,29 @@ +import type { Balance } from '@reown/appkit-common-react-native'; +import { Image } from '../../components/wui-image'; +import { Text } from '../../components/wui-text'; +import { Button } from '../wui-button'; +import styles from './styles'; + +export interface TokenButtonProps { + onPress?: () => void; + token?: Balance; +} + +export function TokenButton({ token, onPress }: TokenButtonProps) { + if (!token) { + return ( + + ); + } + + return ( + + ); +} 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 00000000..7ece57a0 --- /dev/null +++ b/packages/ui/src/composites/wui-token-button/styles.ts @@ -0,0 +1,18 @@ +import { StyleSheet } from 'react-native'; +import { BorderRadius, Spacing } from '../../utils/ThemeUtil'; + +export default StyleSheet.create({ + selectButton: { + height: 40, + paddingHorizontal: Spacing.m + }, + container: { + height: 40 + }, + image: { + width: 24, + height: 24, + borderRadius: BorderRadius.full, + marginRight: Spacing['2xs'] + } +}); diff --git a/packages/ui/src/composites/wui-transaction-visual/index.tsx b/packages/ui/src/composites/wui-transaction-visual/index.tsx new file mode 100644 index 00000000..29d4e7c4 --- /dev/null +++ b/packages/ui/src/composites/wui-transaction-visual/index.tsx @@ -0,0 +1,78 @@ +import type { TransactionImage } from '@reown/appkit-common-react-native'; + +import { FlexView } from '../../layout/wui-flex'; +import { Icon } from '../../components/wui-icon'; +import { Image } from '../../components/wui-image'; +import { useTheme } from '../../hooks/useTheme'; +import styles from './styles'; + +export interface TransactionVisualProps { + images?: TransactionImage[]; + networkSrc?: string; + isAllNFT?: boolean; +} + +export function TransactionVisual({ images, networkSrc, isAllNFT }: TransactionVisualProps) { + const Theme = useTheme(); + const backgroundColor = Theme['bg-200']; + const isFirstNFT = Boolean(images?.[0]?.type === 'NFT'); + const filteredImages = images?.filter(image => image.url); + const [firstImage, secondImage] = filteredImages ?? []; + const hasOneImage = filteredImages?.length === 1; + const hasTwoImages = filteredImages && filteredImages?.length > 1; + + return ( + + {!filteredImages?.length && ( + + + + )} + {hasOneImage && firstImage?.url && ( + + )} + {hasTwoImages && firstImage?.url && secondImage?.url && ( + + + + + + + + + )} + + {networkSrc ? ( + + ) : ( + + )} + + + ); +} diff --git a/packages/ui/src/composites/wui-transaction-visual/styles.ts b/packages/ui/src/composites/wui-transaction-visual/styles.ts new file mode 100644 index 00000000..4bc04db8 --- /dev/null +++ b/packages/ui/src/composites/wui-transaction-visual/styles.ts @@ -0,0 +1,36 @@ +import { StyleSheet } from 'react-native'; +import { BorderRadius, Spacing } from '../../utils/ThemeUtil'; + +export default StyleSheet.create({ + image: { + height: 40, + width: 40, + borderRadius: BorderRadius.full, + marginRight: Spacing.s + }, + imageNft: { + borderRadius: BorderRadius.xxs + }, + halfContainer: { + overflow: 'hidden', + width: 20, + marginRight: 2 + }, + halfRight: { + left: -20 + }, + networkImageContainer: { + position: 'absolute', + bottom: -2, + left: 24, + borderWidth: 2, + borderRadius: BorderRadius.full, + width: 18, + height: 18 + }, + networkImage: { + width: 14, + height: 14, + borderRadius: BorderRadius.full + } +}); diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts index 00c9a3cc..e3c05c2c 100644 --- a/packages/ui/src/index.ts +++ b/packages/ui/src/index.ts @@ -9,8 +9,11 @@ export { Visual, type VisualProps } from './components/wui-visual'; export { Shimmer, type ShimmerProps } from './components/wui-shimmer'; export { AccountButton, type AccountButtonProps } from './composites/wui-account-button'; +export { AccountPill, type AccountPillProps } from './composites/wui-account-pill'; export { ActionEntry, type ActionEntryProps } from './composites/wui-action-entry'; export { Avatar, type AvatarProps } from './composites/wui-avatar'; +export { Balance, type BalanceProps } from './composites/wui-balance'; +export { Banner, type BannerProps } from './composites/wui-banner'; export { Button, type ButtonProps } from './composites/wui-button'; export { CardSelectLoader, @@ -24,6 +27,10 @@ export { type CardSelectProps } from './composites/wui-card-select'; export { Chip, type ChipProps } from './composites/wui-chip'; +export { + CompatibleNetwork, + type CompatibleNetworkProps +} from './composites/wui-compatible-network'; export { ConnectButton, type ConnectButtonProps } from './composites/wui-connect-button'; export { EmailInput, type EmailInputProps } from './composites/wui-email-input'; export { IconBox, type IconBoxProps } from './composites/wui-icon-box'; @@ -33,6 +40,8 @@ export { InputNumeric, type InputNumericProps } from './composites/wui-input-num export { InputText, type InputTextProps } from './composites/wui-input-text'; export { Link, type LinkProps } from './composites/wui-link'; export { ListItem, type ListItemProps } from './composites/wui-list-item'; +export { ListToken, type ListTokenProps } from './composites/wui-list-token'; +export { ListTransaction, type ListTransactionProps } from './composites/wui-list-transaction'; export { ListWallet, type ListWalletProps } from './composites/wui-list-wallet'; export { Logo, type LogoProps } from './composites/wui-logo'; export { LogoSelect, type LogoSelectProps } from './composites/wui-logo-select'; @@ -44,6 +53,7 @@ export { SearchBar, type SearchBarProps } from './composites/wui-search-bar'; export { Snackbar, type SnackbarProps } from './composites/wui-snackbar'; export { Tabs, type TabsProps } from './composites/wui-tabs'; export { Tag, type TagProps } from './composites/wui-tag'; +export { TokenButton, type TokenButtonProps } from './composites/wui-token-button'; export { Tooltip, type TooltipProps } from './composites/wui-tooltip'; export { WalletImage, type WalletImageProps } from './composites/wui-wallet-image'; @@ -66,6 +76,7 @@ export type { VisualType } from './utils/TypesUtil'; export { UiUtil } from './utils/UiUtil'; +export { TransactionUtil } from './utils/TransactionUtil'; export { Spacing, BorderRadius } from './utils/ThemeUtil'; export { useTheme } from './hooks/useTheme'; diff --git a/packages/ui/src/utils/ThemeUtil.ts b/packages/ui/src/utils/ThemeUtil.ts index 5fa3e4fb..9c3fbcd7 100644 --- a/packages/ui/src/utils/ThemeUtil.ts +++ b/packages/ui/src/utils/ThemeUtil.ts @@ -155,7 +155,8 @@ export const BorderRadius = { 's': 20, 'm': 28, 'l': 36, - '3xl': 80 + '3xl': 80, + 'full': 100 }; export const IconSize = { diff --git a/packages/ui/src/utils/TransactionUtil.ts b/packages/ui/src/utils/TransactionUtil.ts new file mode 100644 index 00000000..b0537407 --- /dev/null +++ b/packages/ui/src/utils/TransactionUtil.ts @@ -0,0 +1,173 @@ +import { DateUtil } from '@reown/appkit-common-react-native'; +import type { + TransactionTransfer, + Transaction, + TransactionImage +} from '@reown/appkit-common-react-native'; +import type { TransactionType } from './TypesUtil'; +import { UiUtil } from './UiUtil'; + +// -- Helpers --------------------------------------------- // +const FLOAT_FIXED_VALUE = 2; +const SMALL_FLOAT_FIXED_VALUE = 4; +const plusTypes: TransactionType[] = ['receive', 'deposit', 'borrow', 'claim']; +const minusTypes: TransactionType[] = ['withdraw', 'repay', 'burn']; + +export const TransactionUtil = { + getTransactionGroupTitle(year: string, month: string) { + const currentYear = DateUtil.getYear().toString(); + const monthName = DateUtil.getMonth(parseInt(month)); + const isCurrentYear = year === currentYear; + const groupTitle = isCurrentYear ? monthName : `${monthName} ${year}`; + + return groupTitle; + }, + + getTransactionImages(transfers: TransactionTransfer[]): TransactionImage[] { + const [transfer, secondTransfer] = transfers; + const isAllNFT = Boolean(transfer) && transfers?.every(item => Boolean(item.nft_info)); + const haveMultipleTransfers = transfers?.length > 1; + const haveTwoTransfers = transfers?.length === 2; + + if (haveTwoTransfers && !isAllNFT) { + return [this.getTransactionImage(transfer), this.getTransactionImage(secondTransfer)]; + } + + if (haveMultipleTransfers) { + return transfers.map(item => this.getTransactionImage(item)); + } + + return [this.getTransactionImage(transfer)]; + }, + + getTransactionImage(transfer?: TransactionTransfer): TransactionImage { + return { + type: TransactionUtil.getTransactionTransferTokenType(transfer), + url: TransactionUtil.getTransactionImageURL(transfer) + }; + }, + + getTransactionImageURL(transfer: TransactionTransfer | undefined) { + let imageURL; + const isNFT = Boolean(transfer?.nft_info); + const isFungible = Boolean(transfer?.fungible_info); + + if (transfer && isNFT) { + imageURL = transfer?.nft_info?.content?.preview?.url; + } else if (transfer && isFungible) { + imageURL = transfer?.fungible_info?.icon?.url; + } + + return imageURL; + }, + + getTransactionTransferTokenType(transfer?: TransactionTransfer): 'FUNGIBLE' | 'NFT' | undefined { + if (transfer?.fungible_info) { + return 'FUNGIBLE'; + } else if (transfer?.nft_info) { + return 'NFT'; + } + + return undefined; + }, + + getTransactionDescriptions(transaction: Transaction) { + const type = transaction?.metadata?.operationType as TransactionType; + + const transfers = transaction?.transfers; + const haveTransfer = transaction?.transfers?.length > 0; + const haveMultipleTransfers = transaction?.transfers?.length > 1; + const isSendOrReceive = type === 'send' || type === 'receive'; + const isFungible = + haveTransfer && transfers?.every(transfer => Boolean(transfer?.fungible_info)); + const [firstTransfer, secondTransfer] = transfers; + + let firstDescription = this.getTransferDescription(firstTransfer); + let secondDescription = this.getTransferDescription(secondTransfer); + + if (!haveTransfer) { + if (isSendOrReceive && isFungible) { + firstDescription = UiUtil.getTruncateString({ + string: transaction?.metadata.sentFrom, + charsStart: 4, + charsEnd: 6, + truncate: 'middle' + }); + secondDescription = UiUtil.getTruncateString({ + string: transaction?.metadata.sentTo, + charsStart: 4, + charsEnd: 6, + truncate: 'middle' + }); + + return [firstDescription, secondDescription]; + } + + return [transaction.metadata.status]; + } + + if (haveMultipleTransfers) { + return transfers.map(item => this.getTransferDescription(item)); + } + + let prefix = ''; + if (plusTypes.includes(type)) { + prefix = '+'; + } else if (minusTypes.includes(type)) { + prefix = '-'; + } + + firstDescription = prefix.concat(firstDescription); + + if (isSendOrReceive) { + const isSend = type === 'send'; + const address = UiUtil.getTruncateString({ + string: isSend ? transaction.metadata.sentTo : transaction.metadata.sentFrom, + charsStart: 4, + charsEnd: 4, + truncate: 'middle' + }); + const arrow = isSend ? '→' : '←'; + firstDescription = firstDescription.concat(` ${arrow} ${address}`); + } + + return [firstDescription]; + }, + + getTransferDescription(transfer?: TransactionTransfer) { + let description = ''; + + if (!transfer) { + return description; + } + + if (transfer?.nft_info) { + description = transfer?.nft_info?.name || '-'; + } else if (transfer?.fungible_info) { + description = this.getFungibleTransferDescription(transfer) ?? '-'; + } + + return description; + }, + + getFungibleTransferDescription(transfer?: TransactionTransfer) { + if (!transfer) { + return null; + } + + const quantity = this.getQuantityFixedValue(transfer?.quantity.numeric); + const description = [quantity, transfer?.fungible_info?.symbol].join(' ').trim(); + + return description; + }, + + getQuantityFixedValue(value: string | undefined) { + if (!value) { + return null; + } + + const parsedValue = parseFloat(value); + + return parsedValue.toFixed(parsedValue > 1 ? FLOAT_FIXED_VALUE : SMALL_FLOAT_FIXED_VALUE); + } +}; diff --git a/packages/ui/src/utils/TypesUtil.ts b/packages/ui/src/utils/TypesUtil.ts index 10a14ff0..01a88675 100644 --- a/packages/ui/src/utils/TypesUtil.ts +++ b/packages/ui/src/utils/TypesUtil.ts @@ -91,14 +91,27 @@ export type ColorType = | 'error-100' | 'fg-100' | 'fg-150' + | 'fg-175' | 'fg-200' | 'fg-250' | 'fg-275' | 'fg-300' + | 'accent-glass-020' + | 'accent-glass-015' + | 'accent-glass-010' + | 'accent-glass-005' | 'gray-glass-020' + | 'gray-glass-010' + | 'gray-glass-005' | 'inverse-000' | 'inverse-100' - | 'success-100'; + | 'success-100' + | 'teal-100' + | 'magenta-100' + | 'indigo-100' + | 'orange-100' + | 'purple-100' + | 'yellow-100'; export type SizeType = 'xl' | 'lg' | 'md' | 'sm' | 'xs' | 'xxs'; @@ -112,6 +125,7 @@ export type IconType = | 'allWallets' | 'apple' | 'arrowBottom' + | 'arrowBottomCircle' | 'arrowLeft' | 'arrowRight' | 'arrowTop' @@ -146,6 +160,7 @@ export type IconType = | 'networkPlaceholder' | 'nftPlaceholder' | 'off' + | 'paperplane' | 'qrCode' | 'refresh' | 'search' @@ -193,7 +208,7 @@ export type CardSelectType = 'wallet' | 'network'; export type TabOptionType = { icon: IconType; - label: string; + label?: string; }; export type SpacingType = @@ -238,3 +253,22 @@ export type TruncateOptions = { charsEnd: number; truncate: TruncateType; }; + +export type TransactionType = + | 'approve' + | 'bought' + | 'borrow' + | 'burn' + | 'cancel' + | 'claim' + | 'deploy' + | 'deposit' + | 'execute' + | 'mint' + | 'receive' + | 'repay' + | 'send' + | 'stake' + | 'trade' + | 'unstake' + | 'withdraw'; diff --git a/packages/ui/src/utils/UiUtil.ts b/packages/ui/src/utils/UiUtil.ts index e4678f6b..bca68b00 100644 --- a/packages/ui/src/utils/UiUtil.ts +++ b/packages/ui/src/utils/UiUtil.ts @@ -68,5 +68,23 @@ export const UiUtil = { getWalletName(name: string, short = true) { return short ? name.split(' ')[0] : name; + }, + + 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/wagmi/src/client.ts b/packages/wagmi/src/client.ts index aa5e3b84..804ab256 100644 --- a/packages/wagmi/src/client.ts +++ b/packages/wagmi/src/client.ts @@ -1,16 +1,24 @@ -import { formatUnits, type Hex } from 'viem'; +import { formatUnits, type Hex, parseUnits } from 'viem'; import { type GetAccountReturnType, + type GetEnsAddressReturnType, connect, disconnect, signMessage, + getAccount, switchChain, watchAccount, watchConnectors, getEnsName, getEnsAvatar as wagmiGetEnsAvatar, - getBalance + getEnsAddress as wagmiGetEnsAddress, + getBalance, + prepareTransactionRequest, + sendTransaction as wagmiSendTransaction, + waitForTransactionReceipt, + writeContract as wagmiWriteContract } from '@wagmi/core'; +import { normalize } from 'viem/ens'; import { mainnet, type Chain } from '@wagmi/core/chains'; import { EthereumProvider, OPTIONAL_METHODS } from '@walletconnect/ethereum-provider'; import { @@ -22,8 +30,10 @@ import { type LibraryOptions, type NetworkControllerClient, type PublicStateControllerState, + type SendTransactionArgs, type Token, - AppKitScaffold + AppKitScaffold, + type WriteContractArgs } from '@reown/appkit-scaffold-react-native'; import { ConstantsUtil, @@ -31,12 +41,13 @@ import { PresetsUtil, StorageUtil } from '@reown/appkit-scaffold-utils-react-native'; -import { NetworkUtil } from '@reown/appkit-common-react-native'; +import { NetworkUtil, NamesUtil } from '@reown/appkit-common-react-native'; import { type AppKitSIWEClient } from '@reown/appkit-siwe-react-native'; import { getCaipDefaultChain, getAuthCaipNetworks, - getWalletConnectCaipNetworks + getWalletConnectCaipNetworks, + requireCaipAddress } from './utils/helpers'; import { defaultWagmiConfig } from './utils/defaultWagmiConfig'; @@ -230,6 +241,87 @@ export class AppKit extends AppKitScaffold { const { SIWEController } = await import('@reown/appkit-siwe-react-native'); await SIWEController.signOut(); } + }, + + sendTransaction: async (data: SendTransactionArgs) => { + const { chainId } = getAccount(this.wagmiConfig); + + const txParams = { + account: data.address, + to: data.to, + value: data.value, + gas: data.gas, + gasPrice: data.gasPrice, + data: data.data, + chainId, + type: 'legacy' as const + }; + + await prepareTransactionRequest(this.wagmiConfig, txParams); + const tx = await wagmiSendTransaction(this.wagmiConfig, txParams); + + await waitForTransactionReceipt(this.wagmiConfig, { hash: tx, timeout: 25000 }); + + return tx; + }, + + writeContract: async (data: WriteContractArgs) => { + const caipAddress = this.getCaipAddress() || ''; + const account = requireCaipAddress(caipAddress); + const chainId = NetworkUtil.caipNetworkIdToNumber(this.getCaipNetwork()?.id); + + const tx = await wagmiWriteContract(wagmiConfig, { + chainId, + address: data.tokenAddress, + account, + abi: data.abi, + functionName: data.method, + args: [data.receiverAddress, data.tokenAmount] + }); + + return tx; + }, + + parseUnits, + + formatUnits, + + getEnsAddress: async (value: string) => { + try { + if (!this.wagmiConfig) { + throw new Error( + 'networkControllerClient:getApprovedCaipNetworksData - wagmiConfig is undefined' + ); + } + const chainId = Number(NetworkUtil.caipNetworkIdToNumber(this.getCaipNetwork()?.id)); + let ensName: boolean | GetEnsAddressReturnType = false; + let wcName: boolean | string = false; + if (NamesUtil.isReownName(value)) { + wcName = (await this.resolveReownName(value)) || false; + } + if (chainId === 1) { + ensName = await wagmiGetEnsAddress(this.wagmiConfig, { + name: normalize(value), + chainId + }); + } + + return ensName || wcName || false; + } catch { + return false; + } + }, + getEnsAvatar: async (value: string) => { + const chainId = Number(NetworkUtil.caipNetworkIdToNumber(this.getCaipNetwork()?.id)); + if (chainId !== mainnet.id) { + return false; + } + const avatar = await wagmiGetEnsAvatar(this.wagmiConfig, { + name: normalize(value), + chainId + }); + + return avatar || false; } }; diff --git a/packages/wagmi/src/utils/helpers.ts b/packages/wagmi/src/utils/helpers.ts index 94e9828d..791ff854 100644 --- a/packages/wagmi/src/utils/helpers.ts +++ b/packages/wagmi/src/utils/helpers.ts @@ -7,7 +7,7 @@ import { PresetsUtil, ConstantsUtil } from '@reown/appkit-scaffold-utils-react-n import type { Connector } from '@wagmi/core'; import { EthereumProvider } from '@walletconnect/ethereum-provider'; import type { AppKitClientOptions } from '../client'; -import { http } from 'viem'; +import { http, type Hex } from 'viem'; export function getCaipDefaultChain(chain?: AppKitClientOptions['defaultChain']) { if (!chain) { @@ -56,3 +56,15 @@ export function getTransport({ chainId, projectId }: { chainId: number; projectI return http(`${RPC_URL}/v1/?chainId=${ConstantsUtil.EIP155}:${chainId}&projectId=${projectId}`); } + +export function requireCaipAddress(caipAddress: string) { + if (!caipAddress) { + throw new Error('No CAIP address provided'); + } + const account = caipAddress.split(':')[2] as Hex; + if (!account) { + throw new Error('Invalid CAIP address'); + } + + return account; +} diff --git a/packages/wallet/src/AppKitAuthWebview.tsx b/packages/wallet/src/AppKitAuthWebview.tsx index 72e18ba2..307bc76c 100644 --- a/packages/wallet/src/AppKitAuthWebview.tsx +++ b/packages/wallet/src/AppKitAuthWebview.tsx @@ -8,11 +8,14 @@ import { OptionsController, ModalController, type OptionsControllerState, - StorageUtil + StorageUtil, + RouterController } from '@reown/appkit-core-react-native'; import { useTheme, BorderRadius } from '@reown/appkit-ui-react-native'; import type { AppKitFrameProvider } from './AppKitFrameProvider'; -import { AppKitFrameConstants, AppKitFrameRpcConstants } from './AppKitFrameConstants'; +import { AppKitFrameConstants } from './AppKitFrameConstants'; +import { AppKitFrameHelpers } from './AppKitFrameHelpers'; +import type { AppKitFrameTypes } from './AppKitFrameTypes'; const AnimatedSafeAreaView = Animated.createAnimatedComponent(SafeAreaView); @@ -21,7 +24,7 @@ export function AuthWebview() { const Theme = useTheme(); const { connectors } = useSnapshot(ConnectorController.state); const { projectId, sdkVersion } = useSnapshot(OptionsController.state) as OptionsControllerState; - const [isVisible, setIsVisible] = useState(false); + const [isWebviewVisibile, setIsWebviewVisible] = useState(false); const [isBackdropVisible, setIsBackdropVisible] = useState(false); const animatedHeight = useRef(new Animated.Value(0)); const backdropOpacity = useRef(new Animated.Value(0)); @@ -49,14 +52,37 @@ export function AuthWebview() { provider.onMessage(event); - provider.onRpcRequest(event, () => { - if (!AppKitFrameRpcConstants.SAFE_RPC_METHODS.includes(event.payload.method)) { - setIsVisible(true); + provider.onRpcRequest((request: AppKitFrameTypes.RPCRequest) => { + if (AppKitFrameHelpers.checkIfRequestExists(request)) { + if (!AppKitFrameHelpers.checkIfRequestIsAllowed(request)) { + setIsWebviewVisible(true); + } } }); - provider.onRpcResponse(event, () => { - setIsVisible(false); + provider.onRpcSuccess((_, request) => { + const isSafeRequest = AppKitFrameHelpers.checkIfRequestIsSafe(request); + if (isSafeRequest) { + return; + } + + if (RouterController.state.transactionStack.length === 0) { + ModalController.close(); + } else { + RouterController?.popTransactionStack(); + } + setIsWebviewVisible(false); + }); + + provider.onRpcError(() => { + if (ModalController.state.open) { + if (RouterController.state.transactionStack.length === 0) { + ModalController.close(); + } else { + RouterController?.popTransactionStack(true); + } + } + setIsWebviewVisible(false); }); provider.onIsConnected(event, () => { @@ -78,27 +104,27 @@ export function AuthWebview() { useEffect(() => { Animated.timing(animatedHeight.current, { - toValue: isVisible ? 1 : 0, + toValue: isWebviewVisibile ? 1 : 0, duration: 200, useNativeDriver: false }).start(); Animated.timing(webviewOpacity.current, { - toValue: isVisible ? 1 : 0, + toValue: isWebviewVisibile ? 1 : 0, duration: 300, useNativeDriver: false }).start(); - if (isVisible) { + if (isWebviewVisibile) { setIsBackdropVisible(true); } Animated.timing(backdropOpacity.current, { - toValue: isVisible ? 0.7 : 0, + toValue: isWebviewVisibile ? 0.7 : 0, duration: 300, useNativeDriver: false - }).start(() => setIsBackdropVisible(isVisible)); - }, [animatedHeight, backdropOpacity, isVisible, setIsBackdropVisible]); + }).start(() => setIsBackdropVisible(isWebviewVisibile)); + }, [animatedHeight, backdropOpacity, isWebviewVisibile, setIsBackdropVisible]); useEffect(() => { provider?.setWebviewRef(webviewRef); diff --git a/packages/wallet/src/AppKitFrameConstants.ts b/packages/wallet/src/AppKitFrameConstants.ts index 5ddd69ff..a0a13d0f 100644 --- a/packages/wallet/src/AppKitFrameConstants.ts +++ b/packages/wallet/src/AppKitFrameConstants.ts @@ -105,9 +105,17 @@ export const AppKitFrameRpcConstants = { 'eth_newPendingTransactionFilter', 'eth_sendRawTransaction', 'eth_syncing', - 'eth_uninstallFilter' + 'eth_uninstallFilter', + 'wallet_getCapabilities', + 'wallet_getCallsStatus' + ], + NOT_SAFE_RPC_METHODS: [ + 'personal_sign', + 'eth_signTypedData_v4', + 'eth_sendTransaction', + 'wallet_sendCalls', + 'wallet_grantPermissions' ], - NOT_SAFE_RPC_METHODS: ['personal_sign', 'eth_signTypedData_v4', 'eth_sendTransaction'], GET_CHAIN_ID: 'eth_chainId', RPC_METHOD_NOT_ALLOWED_MESSAGE: 'Requested RPC call is not allowed', RPC_METHOD_NOT_ALLOWED_UI_MESSAGE: 'Action not allowed' diff --git a/packages/wallet/src/AppKitFrameHelpers.ts b/packages/wallet/src/AppKitFrameHelpers.ts index 297a5052..212fd621 100644 --- a/packages/wallet/src/AppKitFrameHelpers.ts +++ b/packages/wallet/src/AppKitFrameHelpers.ts @@ -23,22 +23,18 @@ export const AppKitFrameHelpers = { } }, - checkIfRequestExists(request: unknown) { - const method = this.getRequestMethod(request); - + checkIfRequestExists(request: AppKitFrameTypes.RPCRequest) { return ( - AppKitFrameRpcConstants.NOT_SAFE_RPC_METHODS.includes(method) || - AppKitFrameRpcConstants.SAFE_RPC_METHODS.includes(method) + AppKitFrameRpcConstants.NOT_SAFE_RPC_METHODS.includes(request.method) || + AppKitFrameRpcConstants.SAFE_RPC_METHODS.includes(request.method) ); }, - getRequestMethod(request: unknown) { - return (request as { payload: AppKitFrameTypes.RPCRequest })?.payload?.method; + checkIfRequestIsAllowed(request: AppKitFrameTypes.RPCRequest) { + return AppKitFrameRpcConstants.SAFE_RPC_METHODS.includes(request.method); }, - checkIfRequestIsAllowed(request: unknown) { - const method = this.getRequestMethod(request); - - return AppKitFrameRpcConstants.SAFE_RPC_METHODS.includes(method); + checkIfRequestIsSafe(request: AppKitFrameTypes.RPCRequest) { + return AppKitFrameRpcConstants.SAFE_RPC_METHODS.includes(request.method); } }; diff --git a/packages/wallet/src/AppKitFrameProvider.ts b/packages/wallet/src/AppKitFrameProvider.ts index 05d6a60f..41f336a7 100644 --- a/packages/wallet/src/AppKitFrameProvider.ts +++ b/packages/wallet/src/AppKitFrameProvider.ts @@ -1,3 +1,4 @@ +import { EventEmitter } from 'events'; import type { RefObject } from 'react'; import type WebView from 'react-native-webview'; import { CoreHelperUtil } from '@reown/appkit-core-react-native'; @@ -8,25 +9,6 @@ import { AppKitFrameHelpers } from './AppKitFrameHelpers'; import { AppKitFrameSchema } from './AppKitFrameSchema'; import { AuthWebview } from './AppKitAuthWebview'; -// -- Types ----------------------------------------------------------- -type Resolver = { resolve: (value: T) => void; reject: (reason?: unknown) => void } | undefined; -type ConnectEmailResolver = Resolver; -type ConnectDeviceResolver = Resolver; -type ConnectOtpResolver = Resolver; -type ConnectResolver = Resolver; -type DisconnectResolver = Resolver; -type IsConnectedResolver = Resolver; -type GetChainIdResolver = Resolver; -type SwitchChainResolver = Resolver; -type RpcRequestResolver = Resolver; -type UpdateEmailResolver = Resolver; -type UpdateEmailPrimaryOtpResolver = Resolver; -type UpdateEmailSecondaryOtpResolver = Resolver< - AppKitFrameTypes.Responses['FrameUpdateEmailSecondaryOtpResolver'] ->; -type SyncThemeResolver = Resolver; -type SyncDappDataResolver = Resolver; - // -- Provider -------------------------------------------------------- export class AppKitFrameProvider { private webviewRef: RefObject | undefined; @@ -37,6 +19,13 @@ export class AppKitFrameProvider { private email: string | undefined; + private rpcRequestHandler?: (request: AppKitFrameTypes.RPCRequest) => void; + private rpcSuccessHandler?: ( + response: AppKitFrameTypes.RPCResponse, + request: AppKitFrameTypes.RPCRequest + ) => void; + private rpcErrorHandler?: (error: Error, request: AppKitFrameTypes.RPCRequest) => void; + public webviewLoadPromise: Promise; public webviewLoadPromiseResolver: @@ -48,33 +37,11 @@ export class AppKitFrameProvider { public AuthView = AuthWebview; - private connectEmailResolver: ConnectEmailResolver = undefined; - - private connectDeviceResolver: ConnectDeviceResolver = undefined; - - private connectOtpResolver: ConnectOtpResolver | undefined = undefined; - - private connectResolver: ConnectResolver = undefined; - - private disconnectResolver: DisconnectResolver = undefined; - - private isConnectedResolver: IsConnectedResolver = undefined; - - private getChainIdResolver: GetChainIdResolver = undefined; - - private switchChainResolver: SwitchChainResolver = undefined; - - private rpcRequestResolver: RpcRequestResolver = undefined; - - private updateEmailResolver: UpdateEmailResolver = undefined; - - private updateEmailPrimaryOtpResolver: UpdateEmailPrimaryOtpResolver = undefined; - - private updateEmailSecondaryOtpResolver: UpdateEmailSecondaryOtpResolver = undefined; + private openRpcRequests: Array< + AppKitFrameTypes.RPCRequest & { abortController: AbortController } + > = []; - private syncThemeResolver: SyncThemeResolver = undefined; - - private syncDappDataResolver: SyncDappDataResolver = undefined; + public events: EventEmitter = new EventEmitter(); public constructor(projectId: string, metadata: AppKitFrameTypes.Metadata) { this.webviewLoadPromise = new Promise((resolve, reject) => { @@ -92,72 +59,9 @@ export class AppKitFrameProvider { this.webviewRef = webviewRef; } - public onMessage(e: AppKitFrameTypes.FrameEvent) { - this.onFrameEvent(e, event => { - // console.log('💻 received', e); // eslint-disable-line no-console - switch (event.type) { - case AppKitFrameConstants.FRAME_CONNECT_EMAIL_SUCCESS: - return this.onConnectEmailSuccess(event); - case AppKitFrameConstants.FRAME_CONNECT_EMAIL_ERROR: - return this.onConnectEmailError(event); - case AppKitFrameConstants.FRAME_CONNECT_DEVICE_SUCCESS: - return this.onConnectDeviceSuccess(); - case AppKitFrameConstants.FRAME_CONNECT_DEVICE_ERROR: - return this.onConnectDeviceError(event); - case AppKitFrameConstants.FRAME_CONNECT_OTP_SUCCESS: - return this.onConnectOtpSuccess(); - case AppKitFrameConstants.FRAME_CONNECT_OTP_ERROR: - return this.onConnectOtpError(event); - case AppKitFrameConstants.FRAME_GET_USER_SUCCESS: - return this.onConnectSuccess(event); - case AppKitFrameConstants.FRAME_GET_USER_ERROR: - return this.onConnectError(event); - case AppKitFrameConstants.FRAME_IS_CONNECTED_SUCCESS: - return this.onIsConnectedSuccess(event); - case AppKitFrameConstants.FRAME_IS_CONNECTED_ERROR: - return this.onIsConnectedError(event); - case AppKitFrameConstants.FRAME_GET_CHAIN_ID_SUCCESS: - return this.onGetChainIdSuccess(event); - case AppKitFrameConstants.FRAME_GET_CHAIN_ID_ERROR: - return this.onGetChainIdError(event); - case AppKitFrameConstants.FRAME_SIGN_OUT_SUCCESS: - return this.onSignOutSuccess(); - case AppKitFrameConstants.FRAME_SIGN_OUT_ERROR: - return this.onSignOutError(event); - case AppKitFrameConstants.FRAME_SWITCH_NETWORK_SUCCESS: - return this.onSwitchChainSuccess(event); - case AppKitFrameConstants.FRAME_SWITCH_NETWORK_ERROR: - return this.onSwitchChainError(event); - case AppKitFrameConstants.FRAME_RPC_REQUEST_SUCCESS: - return this.onRpcRequestSuccess(event); - case AppKitFrameConstants.FRAME_RPC_REQUEST_ERROR: - return this.onRpcRequestError(event); - case AppKitFrameConstants.FRAME_SESSION_UPDATE: - return this.onSessionUpdate(event); - case AppKitFrameConstants.FRAME_UPDATE_EMAIL_SUCCESS: - return this.onUpdateEmailSuccess(event); - case AppKitFrameConstants.FRAME_UPDATE_EMAIL_ERROR: - return this.onUpdateEmailError(event); - case AppKitFrameConstants.FRAME_UPDATE_EMAIL_PRIMARY_OTP_SUCCESS: - return this.onUpdateEmailPrimaryOtpSuccess(); - case AppKitFrameConstants.FRAME_UPDATE_EMAIL_PRIMARY_OTP_ERROR: - return this.onUpdateEmailPrimaryOtpError(event); - case AppKitFrameConstants.FRAME_UPDATE_EMAIL_SECONDARY_OTP_SUCCESS: - return this.onUpdateEmailSecondaryOtpSuccess(event); - case AppKitFrameConstants.FRAME_UPDATE_EMAIL_SECONDARY_OTP_ERROR: - return this.onUpdateEmailSecondaryOtpError(event); - case AppKitFrameConstants.FRAME_SYNC_THEME_SUCCESS: - return this.onSyncThemeSuccess(); - case AppKitFrameConstants.FRAME_SYNC_THEME_ERROR: - return this.onSyncThemeError(event); - case AppKitFrameConstants.FRAME_SYNC_DAPP_DATA_SUCCESS: - return this.onSyncDappDataSuccess(); - case AppKitFrameConstants.FRAME_SYNC_DAPP_DATA_ERROR: - return this.onSyncDappDataError(event); - default: - return null; - } - }); + public onMessage(event: AppKitFrameTypes.FrameEvent) { + // console.log('💻 received', e); // eslint-disable-line no-console + this.events.emit('message', event); } public onWebviewLoaded() { @@ -196,124 +100,141 @@ export class AppKitFrameProvider { } public rejectRpcRequest() { - this.rpcRequestResolver?.reject(); + try { + this.openRpcRequests.forEach(({ abortController, method }) => { + if (!AppKitFrameRpcConstants.SAFE_RPC_METHODS.includes(method)) { + abortController.abort(); + } + }); + this.openRpcRequests = []; + } catch (e) {} } public async connectEmail(payload: AppKitFrameTypes.Requests['AppConnectEmailRequest']) { await this.webviewLoadPromise; await AppKitFrameHelpers.checkIfAllowedToTriggerEmail(); - this.postAppEvent({ type: AppKitFrameConstants.APP_CONNECT_EMAIL, payload }); - return new Promise( - (resolve, reject) => { - this.connectEmailResolver = { resolve, reject }; - } - ); + const response = await this.appEvent<'ConnectEmail'>({ + type: AppKitFrameConstants.APP_CONNECT_EMAIL, + payload + } as AppKitFrameTypes.AppEvent); + + this.setNewLastEmailLoginTime(); + + return response; } public async connectDevice() { await this.webviewLoadPromise; - this.postAppEvent({ type: AppKitFrameConstants.APP_CONNECT_DEVICE }); - return new Promise((resolve, reject) => { - this.connectDeviceResolver = { resolve, reject }; - }); + const response = await this.appEvent<'ConnectDevice'>({ + type: AppKitFrameConstants.APP_CONNECT_DEVICE + } as AppKitFrameTypes.AppEvent); + + return response; } public async connectOtp(payload: AppKitFrameTypes.Requests['AppConnectOtpRequest']) { await this.webviewLoadPromise; - this.postAppEvent({ type: AppKitFrameConstants.APP_CONNECT_OTP, payload }); - return new Promise((resolve, reject) => { - this.connectOtpResolver = { resolve, reject }; - }); + const response = await this.appEvent<'ConnectOtp'>({ + type: AppKitFrameConstants.APP_CONNECT_OTP, + payload + } as AppKitFrameTypes.AppEvent); + + return response; } public async isConnected() { await this.webviewLoadPromise; - this.postAppEvent({ + + const response = await this.appEvent<'IsConnected'>({ type: AppKitFrameConstants.APP_IS_CONNECTED, payload: undefined - }); + } as AppKitFrameTypes.AppEvent); - return new Promise( - (resolve, reject) => { - this.isConnectedResolver = { resolve, reject }; - } - ); + if (!response.isConnected) { + this.deleteEmailLoginCache(); + } + + return response; } public async getChainId() { await this.webviewLoadPromise; - this.postAppEvent({ type: AppKitFrameConstants.APP_GET_CHAIN_ID }); - return new Promise((resolve, reject) => { - this.getChainIdResolver = { resolve, reject }; - }); + const response = await this.appEvent<'GetChainId'>({ + type: AppKitFrameConstants.APP_GET_CHAIN_ID + } as AppKitFrameTypes.AppEvent); + + this.setLastUsedChainId(response.chainId); + + return response; } public async updateEmail(payload: AppKitFrameTypes.Requests['AppUpdateEmailRequest']) { await this.webviewLoadPromise; await AppKitFrameHelpers.checkIfAllowedToTriggerEmail(); - this.postAppEvent({ type: AppKitFrameConstants.APP_UPDATE_EMAIL, payload }); - return new Promise( - (resolve, reject) => { - this.updateEmailResolver = { resolve, reject }; - } - ); + const response = await this.appEvent<'UpdateEmail'>({ + type: AppKitFrameConstants.APP_UPDATE_EMAIL, + payload + } as AppKitFrameTypes.AppEvent); + + this.setNewLastEmailLoginTime(); + + return response; } public async updateEmailPrimaryOtp( payload: AppKitFrameTypes.Requests['AppUpdateEmailPrimaryOtpRequest'] ) { await this.webviewLoadPromise; - this.postAppEvent({ + + const response = await this.appEvent<'UpdateEmailPrimaryOtp'>({ type: AppKitFrameConstants.APP_UPDATE_EMAIL_PRIMARY_OTP, payload - }); + } as AppKitFrameTypes.AppEvent); - return new Promise((resolve, reject) => { - this.updateEmailPrimaryOtpResolver = { resolve, reject }; - }); + return response; } public async updateEmailSecondaryOtp( payload: AppKitFrameTypes.Requests['AppUpdateEmailSecondaryOtpRequest'] ) { await this.webviewLoadPromise; - this.postAppEvent({ + + const response = await this.appEvent<'UpdateEmailSecondaryOtp'>({ type: AppKitFrameConstants.APP_UPDATE_EMAIL_SECONDARY_OTP, payload - }); + } as AppKitFrameTypes.AppEvent); - return new Promise( - (resolve, reject) => { - this.updateEmailSecondaryOtpResolver = { resolve, reject }; - } - ); + this.setEmailLoginSuccess(response.newEmail); + + return response; } public async syncTheme(payload: AppKitFrameTypes.Requests['AppSyncThemeRequest']) { await this.webviewLoadPromise; - this.postAppEvent({ type: AppKitFrameConstants.APP_SYNC_THEME, payload }); - return new Promise((resolve, reject) => { - this.syncThemeResolver = { resolve, reject }; - }); + const response = await this.appEvent<'SyncTheme'>({ + type: AppKitFrameConstants.APP_SYNC_THEME, + payload + } as AppKitFrameTypes.AppEvent); + + return response; } public async syncDappData(payload: AppKitFrameTypes.Requests['AppSyncDappDataRequest']) { await this.webviewLoadPromise; const metadata = payload.metadata ?? this.metadata; - this.postAppEvent({ + + const response = await this.appEvent<'SyncDappData'>({ type: AppKitFrameConstants.APP_SYNC_DAPP_DATA, payload: { ...payload, metadata } - }); + } as AppKitFrameTypes.AppEvent); - return new Promise((resolve, reject) => { - this.syncDappDataResolver = { resolve, reject }; - }); + return response; } // -- Provider Methods ------------------------------------------------ @@ -322,60 +243,74 @@ export class AppKitFrameProvider { const chainId = payload?.chainId ?? lastUsedChain ?? 1; await this.webviewLoadPromise; - this.postAppEvent({ + const response = await this.appEvent<'GetUser'>({ type: AppKitFrameConstants.APP_GET_USER, - payload: { chainId } - }); + payload: { ...payload, chainId } + } as AppKitFrameTypes.AppEvent); - return new Promise((resolve, reject) => { - this.connectResolver = { resolve, reject }; - }); + this.setEmailLoginSuccess(response.email); + this.setLastUsedChainId(response.chainId); + + return response; } public async switchNetwork(chainId: number) { await this.webviewLoadPromise; - this.postAppEvent({ + + const response = await this.appEvent<'SwitchNetwork'>({ type: AppKitFrameConstants.APP_SWITCH_NETWORK, payload: { chainId } - }); + } as AppKitFrameTypes.AppEvent); - return new Promise( - (resolve, reject) => { - this.switchChainResolver = { resolve, reject }; - } - ); + this.setLastUsedChainId(response.chainId); + + return response; } public async disconnect() { await this.webviewLoadPromise; - this.postAppEvent({ type: AppKitFrameConstants.APP_SIGN_OUT }); - return new Promise((resolve, reject) => { - this.disconnectResolver = { resolve, reject }; + const response = await this.appEvent<'SignOut'>({ + type: AppKitFrameConstants.APP_SIGN_OUT }); + + this.deleteEmailLoginCache(); + + return response; } - public async request(req: AppKitFrameTypes.RPCRequest) { - if (AppKitFrameRpcConstants.GET_CHAIN_ID === req.method) { - return await this.getLastUsedChainId(); + public async request(req: AppKitFrameTypes.RPCRequest): Promise { + try { + if (AppKitFrameRpcConstants.GET_CHAIN_ID === req.method) { + return this.getLastUsedChainId(); + } + + this.rpcRequestHandler?.(req); + const response = await this.appEvent<'Rpc'>({ + type: AppKitFrameConstants.APP_RPC_REQUEST, + payload: req + } as AppKitFrameTypes.AppEvent); + this.rpcSuccessHandler?.(response, req); + + return response; + } catch (error) { + this.rpcErrorHandler?.(error as Error, req); + throw error; } - await this.webviewLoadPromise; - this.postAppEvent({ - type: AppKitFrameConstants.APP_RPC_REQUEST, - payload: req - }); + } - return new Promise((resolve, reject) => { - this.rpcRequestResolver = { resolve, reject }; - }); + public onRpcRequest(callback: (request: AppKitFrameTypes.RPCRequest) => void) { + this.rpcRequestHandler = callback; } - public onRpcRequest(event: AppKitFrameTypes.AppEvent, callback: (request: unknown) => void) { - this.onAppEvent(event, appEvent => { - if (appEvent.type.includes(AppKitFrameConstants.RPC_METHOD_KEY)) { - callback(appEvent); - } - }); + public onRpcSuccess( + callback: (response: AppKitFrameTypes.FrameEvent, request: AppKitFrameTypes.RPCRequest) => void + ) { + this.rpcSuccessHandler = callback; + } + + public onRpcError(callback: (error: Error) => void) { + this.rpcErrorHandler = callback; } public onRpcResponse(event: AppKitFrameTypes.FrameEvent, callback: (request: unknown) => void) { @@ -408,193 +343,6 @@ export class AppKitFrameProvider { }); } - // -- Promise Handlers ------------------------------------------------ - private onConnectEmailSuccess( - event: Extract - ) { - this.connectEmailResolver?.resolve(event.payload); - this.setNewLastEmailLoginTime(); - } - - private onConnectEmailError( - event: Extract - ) { - this.connectEmailResolver?.reject(event.payload.message); - } - - private onConnectDeviceSuccess() { - this.connectDeviceResolver?.resolve(undefined); - } - - private onConnectDeviceError( - event: Extract - ) { - this.connectDeviceResolver?.reject(event.payload.message); - } - - private onConnectOtpSuccess() { - this.connectOtpResolver?.resolve(undefined); - } - - private onConnectOtpError( - event: Extract - ) { - this.connectOtpResolver?.reject(event.payload.message); - } - - private onConnectSuccess( - event: Extract - ) { - this.setEmailLoginSuccess(event.payload.email); - this.setLastUsedChainId(event.payload.chainId); - this.connectResolver?.resolve(event.payload); - } - - private onConnectError( - event: Extract - ) { - this.connectResolver?.reject(event.payload.message); - } - - private onIsConnectedSuccess( - event: Extract - ) { - if (!event.payload.isConnected) { - this.deleteEmailLoginCache(); - } - this.isConnectedResolver?.resolve(event.payload); - } - - private onIsConnectedError( - event: Extract - ) { - this.isConnectedResolver?.reject(event.payload.message); - } - - private onGetChainIdSuccess( - event: Extract - ) { - this.setLastUsedChainId(event.payload.chainId); - this.getChainIdResolver?.resolve(event.payload); - } - - private onGetChainIdError( - event: Extract - ) { - this.getChainIdResolver?.reject(event.payload.message); - } - - private onSignOutSuccess() { - this.disconnectResolver?.resolve(undefined); - this.deleteEmailLoginCache(); - } - - private onSignOutError( - event: Extract - ) { - this.disconnectResolver?.reject(event.payload.message); - } - - private onSwitchChainSuccess( - event: Extract - ) { - this.setLastUsedChainId(event.payload.chainId); - this.switchChainResolver?.resolve(event.payload); - } - - private onSwitchChainError( - event: Extract - ) { - this.switchChainResolver?.reject(event.payload.message); - } - - private onRpcRequestSuccess( - event: Extract - ) { - this.rpcRequestResolver?.resolve(event.payload); - } - - private onRpcRequestError( - event: Extract - ) { - this.rpcRequestResolver?.reject(event.payload.message); - } - - private onSessionUpdate( - event: Extract - ) { - const { payload } = event; - if (payload) { - // Ilja TODO: this.setSessionToken(payload.token) - } - } - - private onUpdateEmailSuccess( - event: Extract - ) { - this.updateEmailResolver?.resolve(event.payload); - this.setNewLastEmailLoginTime(); - } - - private onUpdateEmailError( - event: Extract - ) { - this.updateEmailResolver?.reject(event.payload.message); - } - - private onUpdateEmailPrimaryOtpSuccess() { - this.updateEmailPrimaryOtpResolver?.resolve(undefined); - } - - private onUpdateEmailPrimaryOtpError( - event: Extract< - AppKitFrameTypes.FrameEvent, - { type: '@w3m-frame/UPDATE_EMAIL_PRIMARY_OTP_ERROR' } - > - ) { - this.updateEmailPrimaryOtpResolver?.reject(event.payload.message); - } - - private onUpdateEmailSecondaryOtpSuccess( - event: Extract< - AppKitFrameTypes.FrameEvent, - { type: '@w3m-frame/UPDATE_EMAIL_SECONDARY_OTP_SUCCESS' } - > - ) { - const { newEmail } = event.payload; - this.setEmailLoginSuccess(newEmail); - this.updateEmailSecondaryOtpResolver?.resolve({ newEmail }); - } - - private onUpdateEmailSecondaryOtpError( - event: Extract< - AppKitFrameTypes.FrameEvent, - { type: '@w3m-frame/UPDATE_EMAIL_SECONDARY_OTP_ERROR' } - > - ) { - this.updateEmailSecondaryOtpResolver?.reject(event.payload.message); - } - - private onSyncThemeSuccess() { - this.syncThemeResolver?.resolve(undefined); - } - - private onSyncThemeError( - event: Extract - ) { - this.syncThemeResolver?.reject(event.payload.message); - } - - private onSyncDappDataSuccess() { - this.syncDappDataResolver?.resolve(undefined); - } - - private onSyncDappDataError( - event: Extract - ) { - this.syncDappDataResolver?.reject(event.payload.message); - } - // -- Private Methods ------------------------------------------------- private setNewLastEmailLoginTime() { AppKitFrameStorage.set(AppKitFrameConstants.LAST_EMAIL_LOGIN_TIME, Date.now().toString()); @@ -627,6 +375,70 @@ export class AppKitFrameProvider { return undefined; } + private async registerFrameEventHandler( + id: string, + callback: (event: AppKitFrameTypes.FrameEvent) => void, + signal: AbortSignal + ) { + const eventEmitter = this.events; + function eventHandler(data: AppKitFrameTypes.FrameEvent) { + if (!data.type?.includes(AppKitFrameConstants.FRAME_EVENT_KEY)) { + return; + } + const frameEvent = AppKitFrameSchema.frameEvent.parse(data); + if (frameEvent.id === id) { + callback(frameEvent); + eventEmitter.removeListener('message', eventHandler); + } + } + + eventEmitter.addListener('message', eventHandler); + signal.addEventListener('abort', () => { + eventEmitter.removeListener('message', eventHandler); + }); + } + + private async appEvent( + event: Omit + ): Promise { + await this.webviewLoadPromise; + const type = event.type.replace('@w3m-app/', ''); + + return new Promise((resolve, reject) => { + const id = Math.random().toString(36).substring(7); + + this.postAppEvent({ ...event, id } as AppKitFrameTypes.AppEvent); + const abortController = new AbortController(); + if (type === 'RPC_REQUEST') { + const rpcEvent = event as Extract< + AppKitFrameTypes.AppEvent, + { type: '@w3m-app/RPC_REQUEST' } + >; + this.openRpcRequests = [...this.openRpcRequests, { ...rpcEvent.payload, abortController }]; + } + abortController.signal.addEventListener('abort', () => { + if (type === 'RPC_REQUEST') { + reject(new Error('Request was aborted')); + } + }); + + function handler(frameEvent: AppKitFrameTypes.FrameEvent) { + if (frameEvent.type === `@w3m-frame/${type}_SUCCESS`) { + if ('payload' in frameEvent) { + resolve(frameEvent.payload); + } + resolve(undefined as unknown as AppKitFrameTypes.Responses[`Frame${T}Response`]); + } else if (frameEvent.type === `@w3m-frame/${type}_ERROR`) { + if ('payload' in frameEvent) { + reject(new Error(frameEvent.payload?.message || 'An error occurred')); + } + reject(new Error('An error occurred')); + } + } + this.registerFrameEventHandler(id, handler, abortController.signal); + }); + } + private onFrameEvent( event: AppKitFrameTypes.FrameEvent, callback: (event: AppKitFrameTypes.FrameEvent) => void @@ -641,17 +453,6 @@ export class AppKitFrameProvider { callback(frameEvent); } - private onAppEvent( - event: AppKitFrameTypes.AppEvent, - callback: (event: AppKitFrameTypes.AppEvent) => void - ) { - if (!event.type?.includes(AppKitFrameConstants.APP_EVENT_KEY)) { - return; - } - const appEvent = AppKitFrameSchema.appEvent.parse(event); - callback(appEvent); - } - private postAppEvent(event: AppKitFrameTypes.AppEvent) { if (!this.webviewRef?.current) { throw new Error('AppKitFrameProvider: webviewRef is not set'); @@ -679,3 +480,21 @@ export class AppKitFrameProvider { return email; } } + +export interface AppKitFrameProviderMethods { + // Email + connectEmail: AppKitFrameProvider['connectEmail']; + connectOtp: AppKitFrameProvider['connectOtp']; + updateEmail: AppKitFrameProvider['updateEmail']; + updateEmailPrimaryOtp: AppKitFrameProvider['updateEmailPrimaryOtp']; + updateEmailSecondaryOtp: AppKitFrameProvider['updateEmailSecondaryOtp']; + getEmail: AppKitFrameProvider['getEmail']; + + // Social + connectDevice: AppKitFrameProvider['connectDevice']; + + // Misc + syncTheme: AppKitFrameProvider['syncTheme']; + syncDappData: AppKitFrameProvider['syncDappData']; + switchNetwork: AppKitFrameProvider['switchNetwork']; +} diff --git a/packages/wallet/src/AppKitFrameSchema.ts b/packages/wallet/src/AppKitFrameSchema.ts index 6ee16be6..55233864 100644 --- a/packages/wallet/src/AppKitFrameSchema.ts +++ b/packages/wallet/src/AppKitFrameSchema.ts @@ -29,10 +29,10 @@ export const GetTransactionByHashResponse = z.object({ v: z.string(), value: z.string() }); -export const AppSwitchNetworkRequest = z.object({ chainId: z.number() }); +export const AppSwitchNetworkRequest = z.object({ chainId: z.string().or(z.number()) }); export const AppConnectEmailRequest = z.object({ email: z.string().email() }); export const AppConnectOtpRequest = z.object({ otp: z.string() }); -export const AppGetUserRequest = z.object({ chainId: z.optional(z.number()) }); +export const AppGetUserRequest = z.object({ chainId: z.optional(z.string().or(z.number())) }); export const AppUpdateEmailRequest = z.object({ email: z.string().email() }); export const AppUpdateEmailPrimaryOtpRequest = z.object({ otp: z.string() }); export const AppUpdateEmailSecondaryOtpRequest = z.object({ otp: z.string() }); @@ -73,7 +73,7 @@ export const FrameGetUserResponse = z.object({ export const FrameIsConnectedResponse = z.object({ isConnected: z.boolean() }); export const FrameGetChainIdResponse = z.object({ chainId: z.number() }); export const FrameSwitchNetworkResponse = z.object({ chainId: z.number() }); -export const FrameUpdateEmailSecondaryOtpResolver = z.object({ newEmail: z.string().email() }); +export const FrameUpdateEmailSecondaryOtpResponse = z.object({ newEmail: z.string().email() }); export const RpcResponse = z.any(); @@ -258,28 +258,35 @@ export const FrameSession = z.object({ token: z.string() }); +export const EventSchema = z.object({ + // Remove optional once all packages are updated + id: z.string().optional() +}); + export const AppKitFrameSchema = { // -- App Events ----------------------------------------------------------- - appEvent: z - .object({ type: zType('APP_SWITCH_NETWORK'), payload: AppSwitchNetworkRequest }) + appEvent: EventSchema.extend({ + type: zType('APP_SWITCH_NETWORK'), + payload: AppSwitchNetworkRequest + }) - .or(z.object({ type: zType('APP_CONNECT_EMAIL'), payload: AppConnectEmailRequest })) + .or(EventSchema.extend({ type: zType('APP_CONNECT_EMAIL'), payload: AppConnectEmailRequest })) - .or(z.object({ type: zType('APP_CONNECT_DEVICE') })) + .or(EventSchema.extend({ type: zType('APP_CONNECT_DEVICE') })) - .or(z.object({ type: zType('APP_CONNECT_OTP'), payload: AppConnectOtpRequest })) + .or(EventSchema.extend({ type: zType('APP_CONNECT_OTP'), payload: AppConnectOtpRequest })) - .or(z.object({ type: zType('APP_GET_USER'), payload: z.optional(AppGetUserRequest) })) + .or(EventSchema.extend({ type: zType('APP_GET_USER'), payload: z.optional(AppGetUserRequest) })) - .or(z.object({ type: zType('APP_SIGN_OUT') })) + .or(EventSchema.extend({ type: zType('APP_SIGN_OUT') })) - .or(z.object({ type: zType('APP_IS_CONNECTED'), payload: z.optional(FrameSession) })) + .or(EventSchema.extend({ type: zType('APP_IS_CONNECTED'), payload: z.optional(FrameSession) })) - .or(z.object({ type: zType('APP_GET_CHAIN_ID') })) + .or(EventSchema.extend({ type: zType('APP_GET_CHAIN_ID') })) .or( - z.object({ + EventSchema.extend({ type: zType('APP_RPC_REQUEST'), payload: RpcPersonalSignRequest.or(RpcEthSendTransactionRequest) .or(RpcEthAccountsRequest) @@ -322,96 +329,145 @@ export const AppKitFrameSchema = { }) ) - .or(z.object({ type: zType('APP_UPDATE_EMAIL'), payload: AppUpdateEmailRequest })) + .or(EventSchema.extend({ type: zType('APP_UPDATE_EMAIL'), payload: AppUpdateEmailRequest })) .or( - z.object({ + EventSchema.extend({ type: zType('APP_UPDATE_EMAIL_PRIMARY_OTP'), payload: AppUpdateEmailPrimaryOtpRequest }) ) .or( - z.object({ + EventSchema.extend({ type: zType('APP_UPDATE_EMAIL_SECONDARY_OTP'), payload: AppUpdateEmailSecondaryOtpRequest }) ) - .or(z.object({ type: zType('APP_SYNC_THEME'), payload: AppSyncThemeRequest })) + .or(EventSchema.extend({ type: zType('APP_SYNC_THEME'), payload: AppSyncThemeRequest })) - .or(z.object({ type: zType('APP_SYNC_DAPP_DATA'), payload: AppSyncDappDataRequest })), + .or(EventSchema.extend({ type: zType('APP_SYNC_DAPP_DATA'), payload: AppSyncDappDataRequest })), // -- Frame Events --------------------------------------------------------- - frameEvent: z - .object({ type: zType('FRAME_SWITCH_NETWORK_ERROR'), payload: zError, origin: z.string() }) + frameEvent: EventSchema.extend({ + type: zType('FRAME_SWITCH_NETWORK_ERROR'), + payload: zError, + origin: z.string() + }) .or( - z.object({ + EventSchema.extend({ type: zType('FRAME_SWITCH_NETWORK_SUCCESS'), payload: FrameSwitchNetworkResponse, origin: z.string() }) ) - .or(z.object({ type: zType('FRAME_CONNECT_EMAIL_ERROR'), payload: zError, origin: z.string() })) + .or( + EventSchema.extend({ + type: zType('FRAME_CONNECT_EMAIL_ERROR'), + payload: zError, + origin: z.string() + }) + ) .or( - z.object({ + EventSchema.extend({ type: zType('FRAME_CONNECT_EMAIL_SUCCESS'), payload: FrameConnectEmailResponse, origin: z.string() }) ) - .or(z.object({ type: zType('FRAME_CONNECT_OTP_ERROR'), payload: zError, origin: z.string() })) + .or( + EventSchema.extend({ + type: zType('FRAME_CONNECT_OTP_ERROR'), + payload: zError, + origin: z.string() + }) + ) - .or(z.object({ type: zType('FRAME_CONNECT_OTP_SUCCESS'), origin: z.string() })) + .or(EventSchema.extend({ type: zType('FRAME_CONNECT_OTP_SUCCESS'), origin: z.string() })) .or( - z.object({ type: zType('FRAME_CONNECT_DEVICE_ERROR'), payload: zError, origin: z.string() }) + EventSchema.extend({ + type: zType('FRAME_CONNECT_DEVICE_ERROR'), + payload: zError, + origin: z.string() + }) ) - .or(z.object({ type: zType('FRAME_CONNECT_DEVICE_SUCCESS'), origin: z.string() })) + .or(EventSchema.extend({ type: zType('FRAME_CONNECT_DEVICE_SUCCESS'), origin: z.string() })) - .or(z.object({ type: zType('FRAME_GET_USER_ERROR'), payload: zError, origin: z.string() })) + .or( + EventSchema.extend({ + type: zType('FRAME_GET_USER_ERROR'), + payload: zError, + origin: z.string() + }) + ) .or( - z.object({ + EventSchema.extend({ type: zType('FRAME_GET_USER_SUCCESS'), payload: FrameGetUserResponse, origin: z.string() }) ) - .or(z.object({ type: zType('FRAME_SIGN_OUT_ERROR'), payload: zError, origin: z.string() })) + .or( + EventSchema.extend({ + type: zType('FRAME_SIGN_OUT_ERROR'), + payload: zError, + origin: z.string() + }) + ) - .or(z.object({ type: zType('FRAME_SIGN_OUT_SUCCESS'), origin: z.string() })) + .or(EventSchema.extend({ type: zType('FRAME_SIGN_OUT_SUCCESS'), origin: z.string() })) - .or(z.object({ type: zType('FRAME_IS_CONNECTED_ERROR'), payload: zError, origin: z.string() })) + .or( + EventSchema.extend({ + type: zType('FRAME_IS_CONNECTED_ERROR'), + payload: zError, + origin: z.string() + }) + ) .or( - z.object({ + EventSchema.extend({ type: zType('FRAME_IS_CONNECTED_SUCCESS'), payload: FrameIsConnectedResponse, origin: z.string() }) ) - .or(z.object({ type: zType('FRAME_GET_CHAIN_ID_ERROR'), payload: zError, origin: z.string() })) + .or( + EventSchema.extend({ + type: zType('FRAME_GET_CHAIN_ID_ERROR'), + payload: zError, + origin: z.string() + }) + ) .or( - z.object({ + EventSchema.extend({ type: zType('FRAME_GET_CHAIN_ID_SUCCESS'), payload: FrameGetChainIdResponse, origin: z.string() }) ) - .or(z.object({ type: zType('FRAME_RPC_REQUEST_ERROR'), payload: zError, origin: z.string() })) + .or( + EventSchema.extend({ + type: zType('FRAME_RPC_REQUEST_ERROR'), + payload: zError, + origin: z.string() + }) + ) .or( - z.object({ + EventSchema.extend({ type: zType('FRAME_RPC_REQUEST_SUCCESS'), payload: RpcResponse, origin: z.string() @@ -419,13 +475,23 @@ export const AppKitFrameSchema = { ) .or( - z.object({ type: zType('FRAME_SESSION_UPDATE'), payload: FrameSession, origin: z.string() }) + EventSchema.extend({ + type: zType('FRAME_SESSION_UPDATE'), + payload: FrameSession, + origin: z.string() + }) ) - .or(z.object({ type: zType('FRAME_UPDATE_EMAIL_ERROR'), payload: zError, origin: z.string() })) + .or( + EventSchema.extend({ + type: zType('FRAME_UPDATE_EMAIL_ERROR'), + payload: zError, + origin: z.string() + }) + ) .or( - z.object({ + EventSchema.extend({ type: zType('FRAME_UPDATE_EMAIL_SUCCESS'), payload: FrameUpdateEmailResponse, origin: z.string() @@ -433,17 +499,22 @@ export const AppKitFrameSchema = { ) .or( - z.object({ + EventSchema.extend({ type: zType('FRAME_UPDATE_EMAIL_PRIMARY_OTP_ERROR'), payload: zError, origin: z.string() }) ) - .or(z.object({ type: zType('FRAME_UPDATE_EMAIL_PRIMARY_OTP_SUCCESS'), origin: z.string() })) + .or( + EventSchema.extend({ + type: zType('FRAME_UPDATE_EMAIL_PRIMARY_OTP_SUCCESS'), + origin: z.string() + }) + ) .or( - z.object({ + EventSchema.extend({ type: zType('FRAME_UPDATE_EMAIL_SECONDARY_OTP_ERROR'), payload: zError, origin: z.string() @@ -451,20 +522,30 @@ export const AppKitFrameSchema = { ) .or( - z.object({ + EventSchema.extend({ type: zType('FRAME_UPDATE_EMAIL_SECONDARY_OTP_SUCCESS'), - payload: FrameUpdateEmailSecondaryOtpResolver, + payload: FrameUpdateEmailSecondaryOtpResponse, origin: z.string() }) ) - .or(z.object({ type: zType('FRAME_SYNC_THEME_ERROR'), payload: zError, origin: z.string() })) + .or( + EventSchema.extend({ + type: zType('FRAME_SYNC_THEME_ERROR'), + payload: zError, + origin: z.string() + }) + ) - .or(z.object({ type: zType('FRAME_SYNC_THEME_SUCCESS'), origin: z.string() })) + .or(EventSchema.extend({ type: zType('FRAME_SYNC_THEME_SUCCESS'), origin: z.string() })) .or( - z.object({ type: zType('FRAME_SYNC_DAPP_DATA_ERROR'), payload: zError, origin: z.string() }) + EventSchema.extend({ + type: zType('FRAME_SYNC_DAPP_DATA_ERROR'), + payload: zError, + origin: z.string() + }) ) - .or(z.object({ type: zType('FRAME_SYNC_DAPP_DATA_SUCCESS'), origin: z.string() })) + .or(EventSchema.extend({ type: zType('FRAME_SYNC_DAPP_DATA_SUCCESS'), origin: z.string() })) }; diff --git a/packages/wallet/src/AppKitFrameTypes.ts b/packages/wallet/src/AppKitFrameTypes.ts index bc29ab59..256f9f13 100644 --- a/packages/wallet/src/AppKitFrameTypes.ts +++ b/packages/wallet/src/AppKitFrameTypes.ts @@ -48,7 +48,7 @@ import { FrameSession, AppGetUserRequest, AppUpdateEmailRequest, - FrameUpdateEmailSecondaryOtpResolver, + FrameUpdateEmailSecondaryOtpResponse, AppUpdateEmailPrimaryOtpRequest, AppUpdateEmailSecondaryOtpRequest, AppSyncThemeRequest, @@ -80,9 +80,16 @@ export namespace AppKitFrameTypes { FrameGetChainIdResponse: z.infer; FrameGetUserResponse: z.infer; FrameIsConnectedResponse: z.infer; - FrameUpdateEmailSecondaryOtpResolver: z.infer; FrameSwitchNetworkResponse: z.infer; FrameUpdateEmailResponse: z.infer; + FrameConnectOtpResponse: undefined; + FrameSyncThemeResponse: undefined; + FrameSyncDappDataResponse: undefined; + FrameUpdateEmailPrimaryOtpResponse: undefined; + FrameUpdateEmailSecondaryOtpResponse: z.infer; + FrameConnectDeviceResponse: undefined; + FrameSignOutResponse: undefined; + FrameRpcResponse: RPCResponse; } export interface Network { @@ -139,4 +146,20 @@ export namespace AppKitFrameTypes { export type RPCResponse = z.infer; export type FrameSessionType = z.infer; + + export type ProviderRequestType = + | 'GetUser' + | 'ConnectDevice' + | 'ConnectEmail' + | 'ConnectOtp' + | 'SwitchNetwork' + | 'UpdateEmail' + | 'SyncTheme' + | 'SyncDappData' + | 'UpdateEmailPrimaryOtp' + | 'UpdateEmailSecondaryOtp' + | 'GetChainId' + | 'IsConnected' + | 'SignOut' + | 'Rpc'; } diff --git a/packages/wallet/src/index.ts b/packages/wallet/src/index.ts index c2c9f6d5..f77b0949 100644 --- a/packages/wallet/src/index.ts +++ b/packages/wallet/src/index.ts @@ -1,5 +1,5 @@ export { AppKitFrameHelpers } from './AppKitFrameHelpers'; -export { AppKitFrameProvider } from './AppKitFrameProvider'; +export { AppKitFrameProvider, type AppKitFrameProviderMethods } from './AppKitFrameProvider'; export { AppKitFrameSchema } from './AppKitFrameSchema'; export { AppKitFrameConstants, AppKitFrameRpcConstants } from './AppKitFrameConstants'; export { AppKitFrameStorage } from './AppKitFrameStorage'; diff --git a/yarn.lock b/yarn.lock index 528c4e86..e2e5d86c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -168,7 +168,7 @@ __metadata: languageName: node linkType: hard -"@babel/compat-data@npm:^7.20.5, @babel/compat-data@npm:^7.22.6, @babel/compat-data@npm:^7.22.9": +"@babel/compat-data@npm:^7.20.5, @babel/compat-data@npm:^7.22.5, @babel/compat-data@npm:^7.22.6, @babel/compat-data@npm:^7.22.9": version: 7.22.9 resolution: "@babel/compat-data@npm:7.22.9" checksum: 1334264b041f8ad4e33036326970c9c26754eb5c04b3af6c223fe6da988cbb8a8542b5526f49ec1ac488210d2f710484a0e4bcd30256294ae3f261d0141febad @@ -196,7 +196,7 @@ __metadata: languageName: node linkType: hard -"@babel/compat-data@npm:^7.25.2, @babel/compat-data@npm:^7.25.4": +"@babel/compat-data@npm:^7.25.2": version: 7.25.4 resolution: "@babel/compat-data@npm:7.25.4" checksum: 50d79734d584a28c69d6f5b99adfaa064d0f41609a378aef04eb06accc5b44f8520e68549eba3a082478180957b7d5783f1bfb1672e4ae8574e797ce8bae79fa @@ -431,16 +431,6 @@ __metadata: languageName: node linkType: hard -"@babel/helper-builder-binary-assignment-operator-visitor@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/helper-builder-binary-assignment-operator-visitor@npm:7.24.7" - dependencies: - "@babel/traverse": "npm:^7.24.7" - "@babel/types": "npm:^7.24.7" - checksum: 0ed84abf848c79fb1cd4c1ddac12c771d32c1904d87fc3087f33cfdeb0c2e0db4e7892b74b407d9d8d0c000044f3645a7391a781f788da8410c290bb123a1f13 - languageName: node - linkType: hard - "@babel/helper-compilation-targets@npm:^7.20.7, @babel/helper-compilation-targets@npm:^7.22.10, @babel/helper-compilation-targets@npm:^7.22.5, @babel/helper-compilation-targets@npm:^7.22.6": version: 7.22.10 resolution: "@babel/helper-compilation-targets@npm:7.22.10" @@ -480,29 +470,29 @@ __metadata: languageName: node linkType: hard -"@babel/helper-compilation-targets@npm:^7.24.7, @babel/helper-compilation-targets@npm:^7.25.2": - version: 7.25.2 - resolution: "@babel/helper-compilation-targets@npm:7.25.2" +"@babel/helper-compilation-targets@npm:^7.24.8": + version: 7.24.8 + resolution: "@babel/helper-compilation-targets@npm:7.24.8" dependencies: - "@babel/compat-data": "npm:^7.25.2" + "@babel/compat-data": "npm:^7.24.8" "@babel/helper-validator-option": "npm:^7.24.8" browserslist: "npm:^4.23.1" lru-cache: "npm:^5.1.1" semver: "npm:^6.3.1" - checksum: de10e986b5322c9f807350467dc845ec59df9e596a5926a3b5edbb4710d8e3b8009d4396690e70b88c3844fe8ec4042d61436dd4b92d1f5f75655cf43ab07e99 + checksum: 2885c44ef6aaf82b7e4352b30089bb09fbe08ed5ec24eb452c2bdc3c021e2a65ab412f74b3d67ec1398da0356c730b33a2ceca1d67d34c85080d31ca6efa9aec languageName: node linkType: hard -"@babel/helper-compilation-targets@npm:^7.24.8": - version: 7.24.8 - resolution: "@babel/helper-compilation-targets@npm:7.24.8" +"@babel/helper-compilation-targets@npm:^7.25.2": + version: 7.25.2 + resolution: "@babel/helper-compilation-targets@npm:7.25.2" dependencies: - "@babel/compat-data": "npm:^7.24.8" + "@babel/compat-data": "npm:^7.25.2" "@babel/helper-validator-option": "npm:^7.24.8" browserslist: "npm:^4.23.1" lru-cache: "npm:^5.1.1" semver: "npm:^6.3.1" - checksum: 2885c44ef6aaf82b7e4352b30089bb09fbe08ed5ec24eb452c2bdc3c021e2a65ab412f74b3d67ec1398da0356c730b33a2ceca1d67d34c85080d31ca6efa9aec + checksum: de10e986b5322c9f807350467dc845ec59df9e596a5926a3b5edbb4710d8e3b8009d4396690e70b88c3844fe8ec4042d61436dd4b92d1f5f75655cf43ab07e99 languageName: node linkType: hard @@ -544,23 +534,6 @@ __metadata: languageName: node linkType: hard -"@babel/helper-create-class-features-plugin@npm:^7.24.7, @babel/helper-create-class-features-plugin@npm:^7.25.4": - version: 7.25.4 - resolution: "@babel/helper-create-class-features-plugin@npm:7.25.4" - dependencies: - "@babel/helper-annotate-as-pure": "npm:^7.24.7" - "@babel/helper-member-expression-to-functions": "npm:^7.24.8" - "@babel/helper-optimise-call-expression": "npm:^7.24.7" - "@babel/helper-replace-supers": "npm:^7.25.0" - "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.24.7" - "@babel/traverse": "npm:^7.25.4" - semver: "npm:^6.3.1" - peerDependencies: - "@babel/core": ^7.0.0 - checksum: a765d9e0482e13cf96642fa8aa28e6f7d4d7d39f37840d6246e5e10a7c47f47c52d52522edd3073f229449d17ec0db6f9b7b5e398bff6bb0b4994d65957a164c - languageName: node - linkType: hard - "@babel/helper-create-class-features-plugin@npm:^7.24.8": version: 7.24.8 resolution: "@babel/helper-create-class-features-plugin@npm:7.24.8" @@ -593,19 +566,6 @@ __metadata: languageName: node linkType: hard -"@babel/helper-create-regexp-features-plugin@npm:^7.24.7, @babel/helper-create-regexp-features-plugin@npm:^7.25.0, @babel/helper-create-regexp-features-plugin@npm:^7.25.2": - version: 7.25.2 - resolution: "@babel/helper-create-regexp-features-plugin@npm:7.25.2" - dependencies: - "@babel/helper-annotate-as-pure": "npm:^7.24.7" - regexpu-core: "npm:^5.3.1" - semver: "npm:^6.3.1" - peerDependencies: - "@babel/core": ^7.0.0 - checksum: 85a7e3639c118856fb1113f54fb7e3bf7698171ddfd0cd6fccccd5426b3727bc1434fe7f69090441dcde327feef9de917e00d35e47ab820047057518dd675317 - languageName: node - linkType: hard - "@babel/helper-define-polyfill-provider@npm:^0.4.2": version: 0.4.2 resolution: "@babel/helper-define-polyfill-provider@npm:0.4.2" @@ -636,21 +596,6 @@ __metadata: languageName: node linkType: hard -"@babel/helper-define-polyfill-provider@npm:^0.6.2": - version: 0.6.2 - resolution: "@babel/helper-define-polyfill-provider@npm:0.6.2" - dependencies: - "@babel/helper-compilation-targets": "npm:^7.22.6" - "@babel/helper-plugin-utils": "npm:^7.22.5" - debug: "npm:^4.1.1" - lodash.debounce: "npm:^4.0.8" - resolve: "npm:^1.14.2" - peerDependencies: - "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 - checksum: f777fe0ee1e467fdaaac059c39ed203bdc94ef2465fb873316e9e1acfc511a276263724b061e3b0af2f6d7ad3ff174f2bb368fde236a860e0f650fda43d7e022 - languageName: node - linkType: hard - "@babel/helper-environment-visitor@npm:^7.18.9, @babel/helper-environment-visitor@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-environment-visitor@npm:7.22.5" @@ -823,32 +768,32 @@ __metadata: languageName: node linkType: hard -"@babel/helper-module-transforms@npm:^7.24.7, @babel/helper-module-transforms@npm:^7.25.0, @babel/helper-module-transforms@npm:^7.25.2": - version: 7.25.2 - resolution: "@babel/helper-module-transforms@npm:7.25.2" +"@babel/helper-module-transforms@npm:^7.24.8, @babel/helper-module-transforms@npm:^7.24.9": + version: 7.24.9 + resolution: "@babel/helper-module-transforms@npm:7.24.9" dependencies: + "@babel/helper-environment-visitor": "npm:^7.24.7" "@babel/helper-module-imports": "npm:^7.24.7" "@babel/helper-simple-access": "npm:^7.24.7" + "@babel/helper-split-export-declaration": "npm:^7.24.7" "@babel/helper-validator-identifier": "npm:^7.24.7" - "@babel/traverse": "npm:^7.25.2" peerDependencies: "@babel/core": ^7.0.0 - checksum: adaa15970ace0aee5934b5a633789b5795b6229c6a9cf3e09a7e80aa33e478675eee807006a862aa9aa517935d81f88a6db8a9f5936e3a2a40ec75f8062bc329 + checksum: e27bca43bc113731ee4f2b33a4c5bf9c7eebf4d64487b814c305cbd5feb272c29fcd3d79634ba03131ade171e5972bc7ede8dbc83ba0deb02f1e62d318c87770 languageName: node linkType: hard -"@babel/helper-module-transforms@npm:^7.24.8, @babel/helper-module-transforms@npm:^7.24.9": - version: 7.24.9 - resolution: "@babel/helper-module-transforms@npm:7.24.9" +"@babel/helper-module-transforms@npm:^7.25.2": + version: 7.25.2 + resolution: "@babel/helper-module-transforms@npm:7.25.2" dependencies: - "@babel/helper-environment-visitor": "npm:^7.24.7" "@babel/helper-module-imports": "npm:^7.24.7" "@babel/helper-simple-access": "npm:^7.24.7" - "@babel/helper-split-export-declaration": "npm:^7.24.7" "@babel/helper-validator-identifier": "npm:^7.24.7" + "@babel/traverse": "npm:^7.25.2" peerDependencies: "@babel/core": ^7.0.0 - checksum: e27bca43bc113731ee4f2b33a4c5bf9c7eebf4d64487b814c305cbd5feb272c29fcd3d79634ba03131ade171e5972bc7ede8dbc83ba0deb02f1e62d318c87770 + checksum: adaa15970ace0aee5934b5a633789b5795b6229c6a9cf3e09a7e80aa33e478675eee807006a862aa9aa517935d81f88a6db8a9f5936e3a2a40ec75f8062bc329 languageName: node linkType: hard @@ -891,7 +836,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-remap-async-to-generator@npm:^7.18.9, @babel/helper-remap-async-to-generator@npm:^7.22.5": +"@babel/helper-remap-async-to-generator@npm:^7.18.9, @babel/helper-remap-async-to-generator@npm:^7.22.5, @babel/helper-remap-async-to-generator@npm:^7.22.9": version: 7.22.9 resolution: "@babel/helper-remap-async-to-generator@npm:7.22.9" dependencies: @@ -917,19 +862,6 @@ __metadata: languageName: node linkType: hard -"@babel/helper-remap-async-to-generator@npm:^7.24.7, @babel/helper-remap-async-to-generator@npm:^7.25.0": - version: 7.25.0 - resolution: "@babel/helper-remap-async-to-generator@npm:7.25.0" - dependencies: - "@babel/helper-annotate-as-pure": "npm:^7.24.7" - "@babel/helper-wrap-function": "npm:^7.25.0" - "@babel/traverse": "npm:^7.25.0" - peerDependencies: - "@babel/core": ^7.0.0 - checksum: 0d17b5f7bb6a607edc9cc62fff8056dd9f341bf2f919884f97b99170d143022a5e7ae57922c4891e4fc360ad291e708d2f8cd8989f1d3cd7a17600159984f5a6 - languageName: node - linkType: hard - "@babel/helper-replace-supers@npm:^7.22.5, @babel/helper-replace-supers@npm:^7.22.9": version: 7.22.9 resolution: "@babel/helper-replace-supers@npm:7.22.9" @@ -956,19 +888,6 @@ __metadata: languageName: node linkType: hard -"@babel/helper-replace-supers@npm:^7.25.0": - version: 7.25.0 - resolution: "@babel/helper-replace-supers@npm:7.25.0" - dependencies: - "@babel/helper-member-expression-to-functions": "npm:^7.24.8" - "@babel/helper-optimise-call-expression": "npm:^7.24.7" - "@babel/traverse": "npm:^7.25.0" - peerDependencies: - "@babel/core": ^7.0.0 - checksum: b4b6650ab3d56c39a259367cd97f8df2f21c9cebb3716fea7bca40a150f8847bfb82f481e98927c7c6579b48a977b5a8f77318a1c6aeb497f41ecd6dbc3fdfef - languageName: node - linkType: hard - "@babel/helper-simple-access@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-simple-access@npm:7.22.5" @@ -1117,17 +1036,6 @@ __metadata: languageName: node linkType: hard -"@babel/helper-wrap-function@npm:^7.25.0": - version: 7.25.0 - resolution: "@babel/helper-wrap-function@npm:7.25.0" - dependencies: - "@babel/template": "npm:^7.25.0" - "@babel/traverse": "npm:^7.25.0" - "@babel/types": "npm:^7.25.0" - checksum: d54601a98384c191cbc1ff07b03a19e288ef8d5c6bfafe270b2a303d96e7304eb296002921ed464cc1b105a547d1db146eb86b0be617924dee1ba1b379cdc216 - languageName: node - linkType: hard - "@babel/helpers@npm:^7.22.10": version: 7.22.10 resolution: "@babel/helpers@npm:7.22.10" @@ -1273,29 +1181,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-bugfix-firefox-class-in-computed-class-key@npm:^7.25.3": - version: 7.25.3 - resolution: "@babel/plugin-bugfix-firefox-class-in-computed-class-key@npm:7.25.3" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.24.8" - "@babel/traverse": "npm:^7.25.3" - peerDependencies: - "@babel/core": ^7.0.0 - checksum: 814b4d3f102e7556a5053d1acf57ef601cfcff39a2c81b8cdc6a5c842e3cb9838f5925d1466a5f1e6416e74c9c83586a3c07fbd7fb8610a396c2becdf9ae5790 - languageName: node - linkType: hard - -"@babel/plugin-bugfix-safari-class-field-initializer-scope@npm:^7.25.0": - version: 7.25.0 - resolution: "@babel/plugin-bugfix-safari-class-field-initializer-scope@npm:7.25.0" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.24.8" - peerDependencies: - "@babel/core": ^7.0.0 - checksum: 9645a1f47b3750acadb1353c02e71cc712d072aafe5ce115ed3a886bc14c5d9200cfb0b5b5e60e813baa549b800cf798f8714019fd246c699053cf68c428e426 - languageName: node - linkType: hard - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.22.15": version: 7.22.15 resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.22.15" @@ -1307,14 +1192,14 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.25.0": - version: 7.25.0 - resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.25.0" +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": "npm:^7.24.8" + "@babel/helper-plugin-utils": "npm:^7.22.5" peerDependencies: "@babel/core": ^7.0.0 - checksum: ed1ce1c90cac46c01825339fd0f2a96fa071b016fb819d8dfaf8e96300eae30e74870cb47e4dc80d4ce2fb287869f102878b4f3b35bc927fec8b1d0d76bcf612 + checksum: 573bd9b1984d74e3663cb7f5f317646223020107681e8dcffe68b041bd620ebbb35c0cc05f4ee20f2da502d02a9633e2b477596e71f4f7802f72c02e948f38af languageName: node linkType: hard @@ -1331,28 +1216,16 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:7.24.7" +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": "npm:^7.24.7" - "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.24.7" - "@babel/plugin-transform-optional-chaining": "npm:^7.24.7" + "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.22.5" + "@babel/plugin-transform-optional-chaining": "npm:^7.22.5" peerDependencies: "@babel/core": ^7.13.0 - checksum: aeb6e7aa363a47f815cf956ea1053c5dd8b786a17799f065c9688ba4b0051fe7565d258bbe9400bfcbfb3114cb9fda66983e10afe4d750bc70ff75403e15dd36 - languageName: node - linkType: hard - -"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@npm:^7.25.0": - version: 7.25.0 - resolution: "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@npm:7.25.0" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.24.8" - "@babel/traverse": "npm:^7.25.0" - peerDependencies: - "@babel/core": ^7.0.0 - checksum: 45988025537a9d4a27b610fd696a18fd9ba9336621a69b4fb40560eeb10c79657f85c92a37f30c7c8fb29c22970eea0b373315795a891f1a05549a6cfe5a6bfe + checksum: 1e38dcd28d2dc5012f96550a3fa1330d71fc923607ceccc91e83c0b7dd3eaeb4d8c632946909c389964acb3e35c888f81653e2d24f7cc02a83fe39a64ca59e89 languageName: node linkType: hard @@ -1604,17 +1477,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-import-assertions@npm:^7.24.7": - version: 7.25.6 - resolution: "@babel/plugin-syntax-import-assertions@npm:7.25.6" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.24.8" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 55afa63b1b1355bcc1d85a9ad9d2c78983e27beee38e232d5c1ab59eac39127ce3c3817d6686e3ab1d0aff5edd8e38a6852885c65d3e518accdd183a445ef411 - languageName: node - linkType: hard - "@babel/plugin-syntax-import-attributes@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-syntax-import-attributes@npm:7.22.5" @@ -1626,17 +1488,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-import-attributes@npm:^7.24.7": - version: 7.25.6 - resolution: "@babel/plugin-syntax-import-attributes@npm:7.25.6" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.24.8" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 0e9359cf2d117476310961dfcfd7204ed692e933707da10d6194153d3996cd2ea5b7635fc90d720dce3612083af89966bb862561064a509c350320dc98644751 - languageName: node - linkType: hard - "@babel/plugin-syntax-import-meta@npm:^7.10.4, @babel/plugin-syntax-import-meta@npm:^7.8.3": version: 7.10.4 resolution: "@babel/plugin-syntax-import-meta@npm:7.10.4" @@ -1825,14 +1676,17 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-arrow-functions@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/plugin-transform-arrow-functions@npm:7.24.7" +"@babel/plugin-transform-async-generator-functions@npm:^7.22.10": + version: 7.22.10 + resolution: "@babel/plugin-transform-async-generator-functions@npm:7.22.10" dependencies: - "@babel/helper-plugin-utils": "npm:^7.24.7" + "@babel/helper-environment-visitor": "npm:^7.22.5" + "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-remap-async-to-generator": "npm:^7.22.9" + "@babel/plugin-syntax-async-generators": "npm:^7.8.4" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 6ac05a54e5582f34ac6d5dc26499e227227ec1c7fa6fc8de1f3d40c275f140d3907f79bbbd49304da2d7008a5ecafb219d0b71d78ee3290ca22020d878041245 + checksum: d0ea9119838e801752b37c8002ab61664be2c31564033d119d468236fbe63d72a41e37f5e348df41e1d610b787e5c6658a67f8bfea41a9f8f4b7dfde479817b2 languageName: node linkType: hard @@ -1850,20 +1704,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-async-generator-functions@npm:^7.25.4": - version: 7.25.4 - resolution: "@babel/plugin-transform-async-generator-functions@npm:7.25.4" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.24.8" - "@babel/helper-remap-async-to-generator": "npm:^7.25.0" - "@babel/plugin-syntax-async-generators": "npm:^7.8.4" - "@babel/traverse": "npm:^7.25.4" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: efed6f6be90b25ad77c15a622a0dc0b22dbf5d45599c207ab8fbc4e959aef21f574fa467d9cf872e45de664a46c32334e78dee2332d82f5f27e26249a34a0920 - languageName: node - linkType: hard - "@babel/plugin-transform-async-to-generator@npm:^7.20.0, @babel/plugin-transform-async-to-generator@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-async-to-generator@npm:7.22.5" @@ -1877,19 +1717,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-async-to-generator@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/plugin-transform-async-to-generator@npm:7.24.7" - dependencies: - "@babel/helper-module-imports": "npm:^7.24.7" - "@babel/helper-plugin-utils": "npm:^7.24.7" - "@babel/helper-remap-async-to-generator": "npm:^7.24.7" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 83c82e243898875af8457972a26ab29baf8a2078768ee9f35141eb3edff0f84b165582a2ff73e90a9e08f5922bf813dbf15a85c1213654385198f4591c0dc45d - languageName: node - linkType: hard - "@babel/plugin-transform-block-scoped-functions@npm:^7.0.0, @babel/plugin-transform-block-scoped-functions@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-block-scoped-functions@npm:7.22.5" @@ -1901,18 +1728,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-block-scoped-functions@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/plugin-transform-block-scoped-functions@npm:7.24.7" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.24.7" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 113e86de4612ae91773ff5cb6b980f01e1da7e26ae6f6012127415d7ae144e74987bc23feb97f63ba4bc699331490ddea36eac004d76a20d5369e4cc6a7f61cd - languageName: node - linkType: hard - -"@babel/plugin-transform-block-scoping@npm:^7.0.0": +"@babel/plugin-transform-block-scoping@npm:^7.0.0, @babel/plugin-transform-block-scoping@npm:^7.22.10": version: 7.22.10 resolution: "@babel/plugin-transform-block-scoping@npm:7.22.10" dependencies: @@ -1934,17 +1750,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-block-scoping@npm:^7.25.0": - version: 7.25.0 - resolution: "@babel/plugin-transform-block-scoping@npm:7.25.0" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.24.8" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 382931c75a5d0ea560387e76cb57b03461300527e4784efcb2fb62f36c1eb0ab331327b6034def256baa0cad9050925a61f9c0d56261b6afd6a29c3065fb0bd4 - languageName: node - linkType: hard - "@babel/plugin-transform-class-properties@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-class-properties@npm:7.22.5" @@ -1957,18 +1762,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-class-properties@npm:^7.25.4": - version: 7.25.4 - resolution: "@babel/plugin-transform-class-properties@npm:7.25.4" - dependencies: - "@babel/helper-create-class-features-plugin": "npm:^7.25.4" - "@babel/helper-plugin-utils": "npm:^7.24.8" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 0b41bc8a5920d3d17c7c06220b601cf43e0a32ac34f05f05cd0cdf08915e4521b1b707cb1e60942b4fc68a5dfac09f0444a8720e0c72ce76fb039e8ec5263115 - languageName: node - linkType: hard - "@babel/plugin-transform-class-static-block@npm:^7.22.11": version: 7.22.11 resolution: "@babel/plugin-transform-class-static-block@npm:7.22.11" @@ -1982,20 +1775,20 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-class-static-block@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/plugin-transform-class-static-block@npm:7.24.7" +"@babel/plugin-transform-class-static-block@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-class-static-block@npm:7.22.5" dependencies: - "@babel/helper-create-class-features-plugin": "npm:^7.24.7" - "@babel/helper-plugin-utils": "npm:^7.24.7" + "@babel/helper-create-class-features-plugin": "npm:^7.22.5" + "@babel/helper-plugin-utils": "npm:^7.22.5" "@babel/plugin-syntax-class-static-block": "npm:^7.14.5" peerDependencies: "@babel/core": ^7.12.0 - checksum: b0ade39a3d09dce886f79dbd5907c3d99b48167eddb6b9bbde24a0598129654d7017e611c20494cdbea48b07ac14397cd97ea34e3754bbb2abae4e698128eccb + checksum: 23814d00b2966e8dab7a60934622853698b2cb861a8667c006e000d8e5a50aba4d221c52852552562e7f38e32ad5c7778125ef602c2d2f1c4f9d8f790a9f27e9 languageName: node linkType: hard -"@babel/plugin-transform-classes@npm:^7.0.0": +"@babel/plugin-transform-classes@npm:^7.0.0, @babel/plugin-transform-classes@npm:^7.22.6": version: 7.22.6 resolution: "@babel/plugin-transform-classes@npm:7.22.6" dependencies: @@ -2033,22 +1826,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-classes@npm:^7.25.4": - version: 7.25.4 - resolution: "@babel/plugin-transform-classes@npm:7.25.4" - dependencies: - "@babel/helper-annotate-as-pure": "npm:^7.24.7" - "@babel/helper-compilation-targets": "npm:^7.25.2" - "@babel/helper-plugin-utils": "npm:^7.24.8" - "@babel/helper-replace-supers": "npm:^7.25.0" - "@babel/traverse": "npm:^7.25.4" - globals: "npm:^11.1.0" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: c68424d9dd64860825111aa4a4ed5caf29494b7a02ddb9c36351d768c41e8e05127d89274795cdfcade032d9d299e6c677418259df58c71e68f1741583dcf467 - languageName: node - linkType: hard - "@babel/plugin-transform-computed-properties@npm:^7.0.0, @babel/plugin-transform-computed-properties@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-computed-properties@npm:7.22.5" @@ -2061,19 +1838,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-computed-properties@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/plugin-transform-computed-properties@npm:7.24.7" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.24.7" - "@babel/template": "npm:^7.24.7" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 25636dbc1f605c0b8bc60aa58628a916b689473d11551c9864a855142e36742fe62d4a70400ba3b74902338e77fb3d940376c0a0ba154b6b7ec5367175233b49 - languageName: node - linkType: hard - -"@babel/plugin-transform-destructuring@npm:^7.0.0, @babel/plugin-transform-destructuring@npm:^7.20.0": +"@babel/plugin-transform-destructuring@npm:^7.0.0, @babel/plugin-transform-destructuring@npm:^7.20.0, @babel/plugin-transform-destructuring@npm:^7.22.10": version: 7.22.10 resolution: "@babel/plugin-transform-destructuring@npm:7.22.10" dependencies: @@ -2095,17 +1860,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-destructuring@npm:^7.24.8": - version: 7.24.8 - resolution: "@babel/plugin-transform-destructuring@npm:7.24.8" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.24.8" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 804968c1d5f5072c717505296c1e5d5ec33e90550423de66de82bbcb78157156e8470bbe77a04ab8c710a88a06360a30103cf223ac7eff4829adedd6150de5ce - languageName: node - linkType: hard - "@babel/plugin-transform-dotall-regex@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-dotall-regex@npm:7.22.5" @@ -2118,18 +1872,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-dotall-regex@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/plugin-transform-dotall-regex@npm:7.24.7" - dependencies: - "@babel/helper-create-regexp-features-plugin": "npm:^7.24.7" - "@babel/helper-plugin-utils": "npm:^7.24.7" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 793f14c9494972d294b7e7b97b747f47874b6d57d7804d3443c701becf5db192c9311be6a1835c07664486df1f5c60d33196c36fb7e11a53015e476b4c145b33 - languageName: node - linkType: hard - "@babel/plugin-transform-duplicate-keys@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-duplicate-keys@npm:7.22.5" @@ -2141,29 +1883,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-duplicate-keys@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/plugin-transform-duplicate-keys@npm:7.24.7" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.24.7" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 75ff7ec1117ac500e77bf20a144411d39c0fdd038f108eec061724123ce6d1bb8d5bd27968e466573ee70014f8be0043361cdb0ef388f8a182d1d97ad67e51b9 - languageName: node - linkType: hard - -"@babel/plugin-transform-duplicate-named-capturing-groups-regex@npm:^7.25.0": - version: 7.25.0 - resolution: "@babel/plugin-transform-duplicate-named-capturing-groups-regex@npm:7.25.0" - dependencies: - "@babel/helper-create-regexp-features-plugin": "npm:^7.25.0" - "@babel/helper-plugin-utils": "npm:^7.24.8" - peerDependencies: - "@babel/core": ^7.0.0 - checksum: 1c9b57ddd9b33696e88911d0e7975e1573ebc46219c4b30eb1dc746cbb71aedfac6f6dab7fdfdec54dd58f31468bf6ab56b157661ea4ffe58f906d71f89544c8 - languageName: node - linkType: hard - "@babel/plugin-transform-dynamic-import@npm:^7.22.11": version: 7.22.11 resolution: "@babel/plugin-transform-dynamic-import@npm:7.22.11" @@ -2176,15 +1895,15 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-dynamic-import@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/plugin-transform-dynamic-import@npm:7.24.7" +"@babel/plugin-transform-dynamic-import@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-dynamic-import@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": "npm:^7.24.7" + "@babel/helper-plugin-utils": "npm:^7.22.5" "@babel/plugin-syntax-dynamic-import": "npm:^7.8.3" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: eeda48372efd0a5103cb22dadb13563c975bce18ae85daafbb47d57bb9665d187da9d4fe8d07ac0a6e1288afcfcb73e4e5618bf75ff63fddf9736bfbf225203b + checksum: 82fb6fa0b6f7c7760ac21ebcb856a01579c9e64a325d5bb8841591b58b2d92024169f10f4ca2b34b45376999b352974138c94fc1d5cc330e00beeeb1bda51425 languageName: node linkType: hard @@ -2200,18 +1919,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-exponentiation-operator@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/plugin-transform-exponentiation-operator@npm:7.24.7" - dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor": "npm:^7.24.7" - "@babel/helper-plugin-utils": "npm:^7.24.7" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: ace3e11c94041b88848552ba8feb39ae4d6cad3696d439ff51445bd2882d8b8775d85a26c2c0edb9b5e38c9e6013cc11b0dea89ec8f93c7d9d7ee95e3645078c - languageName: node - linkType: hard - "@babel/plugin-transform-export-namespace-from@npm:^7.22.11": version: 7.22.11 resolution: "@babel/plugin-transform-export-namespace-from@npm:7.22.11" @@ -2224,15 +1931,15 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-export-namespace-from@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/plugin-transform-export-namespace-from@npm:7.24.7" +"@babel/plugin-transform-export-namespace-from@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-export-namespace-from@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": "npm:^7.24.7" + "@babel/helper-plugin-utils": "npm:^7.22.5" "@babel/plugin-syntax-export-namespace-from": "npm:^7.8.3" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 4e144d7f1c57bc63b4899dbbbdfed0880f2daa75ea9c7251c7997f106e4b390dc362175ab7830f11358cb21f6b972ca10a43a2e56cd789065f7606b082674c0c + checksum: d5d301dde2d6e7f9e4db12ac70e19153f0e8d17406ad733a8f7d01de77d123588fe90c7f5b8cc086420594ec1e7d20abc5e08323f9ad9704a19c6c87ca03eb59 languageName: node linkType: hard @@ -2248,7 +1955,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-for-of@npm:^7.0.0": +"@babel/plugin-transform-for-of@npm:^7.0.0, @babel/plugin-transform-for-of@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-for-of@npm:7.22.5" dependencies: @@ -2270,18 +1977,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-for-of@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/plugin-transform-for-of@npm:7.24.7" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.24.7" - "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.24.7" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 77629b1173e55d07416f05ba7353caa09d2c2149da2ca26721ab812209b63689d1be45116b68eadc011c49ced59daf5320835b15245eb7ae93ae0c5e8277cfc0 - languageName: node - linkType: hard - "@babel/plugin-transform-function-name@npm:^7.0.0, @babel/plugin-transform-function-name@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-function-name@npm:7.22.5" @@ -2295,19 +1990,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-function-name@npm:^7.25.1": - version: 7.25.1 - resolution: "@babel/plugin-transform-function-name@npm:7.25.1" - dependencies: - "@babel/helper-compilation-targets": "npm:^7.24.8" - "@babel/helper-plugin-utils": "npm:^7.24.8" - "@babel/traverse": "npm:^7.25.1" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: e74912174d5e33d1418b840443c2e226a7b76cc017c1ed20ee30a566e4f1794d4a123be03180da046241576e8b692731807ba1f52608922acf1cb2cb6957593f - languageName: node - linkType: hard - "@babel/plugin-transform-json-strings@npm:^7.22.11": version: 7.22.11 resolution: "@babel/plugin-transform-json-strings@npm:7.22.11" @@ -2320,15 +2002,15 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-json-strings@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/plugin-transform-json-strings@npm:7.24.7" +"@babel/plugin-transform-json-strings@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-json-strings@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": "npm:^7.24.7" + "@babel/helper-plugin-utils": "npm:^7.22.5" "@babel/plugin-syntax-json-strings": "npm:^7.8.3" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 17c72cd5bf3e90e722aabd333559275f3309e3fa0b9cea8c2944ab83ae01502c71a2be05da5101edc02b3fc8df15a8dbb9b861cbfcc8a52bf5e797cf01d3a40a + checksum: 64ee0f3497822d312b609d3b8a5a2617337d1624292e89f5e90fd25b5bc91a20beadfa91730b5b199b5a027284ced5d59748d99e8ab81ee7bdac38236e6b61ca languageName: node linkType: hard @@ -2343,17 +2025,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-literals@npm:^7.25.2": - version: 7.25.2 - resolution: "@babel/plugin-transform-literals@npm:7.25.2" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.24.8" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 0796883217b0885d37e7f6d350773be349e469a812b6bf11ccf862a6edf65103d3e7c849529d65381b441685c12e756751d8c2489a0fd3f8139bb5ef93185f58 - languageName: node - linkType: hard - "@babel/plugin-transform-logical-assignment-operators@npm:^7.22.11": version: 7.22.11 resolution: "@babel/plugin-transform-logical-assignment-operators@npm:7.22.11" @@ -2366,15 +2037,15 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-logical-assignment-operators@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/plugin-transform-logical-assignment-operators@npm:7.24.7" +"@babel/plugin-transform-logical-assignment-operators@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-logical-assignment-operators@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": "npm:^7.24.7" + "@babel/helper-plugin-utils": "npm:^7.22.5" "@babel/plugin-syntax-logical-assignment-operators": "npm:^7.10.4" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: dbe882eb9053931f2ab332c50fc7c2a10ef507d6421bd9831adbb4cb7c9f8e1e5fbac4fbd2e007f6a1bf1df1843547559434012f118084dc0bf42cda3b106272 + checksum: bfacdafa8018d1607897015e1ea0f98edbefee16b4409d5f37c37df0d2058dde2e55586dd79f8479a0cd603ff06272216de077f071bc49c96014edfe1629bd26 languageName: node linkType: hard @@ -2389,14 +2060,15 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-member-expression-literals@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/plugin-transform-member-expression-literals@npm:7.24.7" +"@babel/plugin-transform-modules-amd@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-modules-amd@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": "npm:^7.24.7" + "@babel/helper-module-transforms": "npm:^7.22.5" + "@babel/helper-plugin-utils": "npm:^7.22.5" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: e789ae359bdf2d20e90bedef18dfdbd965c9ebae1cee398474a0c349590fda7c8b874e1a2ceee62e47e5e6ec1730e76b0f24e502164357571854271fc12cc684 + checksum: 157ae3b58a50ca52e361860ecab2b608bc9228ea6c760112a35302990976f8936b8d75a2b21925797eed7b3bab4930a3f447193127afef9a21b7b6463ff0b422 languageName: node linkType: hard @@ -2412,18 +2084,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-modules-amd@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/plugin-transform-modules-amd@npm:7.24.7" - dependencies: - "@babel/helper-module-transforms": "npm:^7.24.7" - "@babel/helper-plugin-utils": "npm:^7.24.7" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 6df7de7fce34117ca4b2fa07949b12274c03668cbfe21481c4037b6300796d50ae40f4f170527b61b70a67f26db906747797e30dbd0d9809a441b6e220b5728f - languageName: node - linkType: hard - "@babel/plugin-transform-modules-commonjs@npm:^7.0.0, @babel/plugin-transform-modules-commonjs@npm:^7.13.8, @babel/plugin-transform-modules-commonjs@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-modules-commonjs@npm:7.22.5" @@ -2450,7 +2110,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-modules-commonjs@npm:^7.24.7, @babel/plugin-transform-modules-commonjs@npm:^7.24.8": +"@babel/plugin-transform-modules-commonjs@npm:^7.24.7": version: 7.24.8 resolution: "@babel/plugin-transform-modules-commonjs@npm:7.24.8" dependencies: @@ -2463,31 +2123,31 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-modules-systemjs@npm:^7.23.0": - version: 7.23.0 - resolution: "@babel/plugin-transform-modules-systemjs@npm:7.23.0" +"@babel/plugin-transform-modules-systemjs@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-modules-systemjs@npm:7.22.5" dependencies: "@babel/helper-hoist-variables": "npm:^7.22.5" - "@babel/helper-module-transforms": "npm:^7.23.0" + "@babel/helper-module-transforms": "npm:^7.22.5" "@babel/helper-plugin-utils": "npm:^7.22.5" - "@babel/helper-validator-identifier": "npm:^7.22.20" + "@babel/helper-validator-identifier": "npm:^7.22.5" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 04c5cef7d6921bb9c9073cea389289099124e78cd1e3b7e020e3c085d486b48efadd9a42c0c0d963a9b1c3d5465c3151229092ea719997e53427f36935c84178 + checksum: 25d7ada275039523541cfc3efd91cd3d9cfc77e7b9dd6a51e7d9ad842d2cb3e0f26aee29426aa56ac72f61247268369680f2bdc1171bb00a16cfd00bbb325a6c languageName: node linkType: hard -"@babel/plugin-transform-modules-systemjs@npm:^7.25.0": - version: 7.25.0 - resolution: "@babel/plugin-transform-modules-systemjs@npm:7.25.0" +"@babel/plugin-transform-modules-systemjs@npm:^7.23.0": + version: 7.23.0 + resolution: "@babel/plugin-transform-modules-systemjs@npm:7.23.0" dependencies: - "@babel/helper-module-transforms": "npm:^7.25.0" - "@babel/helper-plugin-utils": "npm:^7.24.8" - "@babel/helper-validator-identifier": "npm:^7.24.7" - "@babel/traverse": "npm:^7.25.0" + "@babel/helper-hoist-variables": "npm:^7.22.5" + "@babel/helper-module-transforms": "npm:^7.23.0" + "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-validator-identifier": "npm:^7.22.20" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: fca6198da71237e4bb1274b3b67a0c81d56013c9535361242b6bfa87d70a9597854aadb45d4d8203369be4a655e158be2a5d20af0040b1f8d1bfc47db3ad7b68 + checksum: 04c5cef7d6921bb9c9073cea389289099124e78cd1e3b7e020e3c085d486b48efadd9a42c0c0d963a9b1c3d5465c3151229092ea719997e53427f36935c84178 languageName: node linkType: hard @@ -2503,18 +2163,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-modules-umd@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/plugin-transform-modules-umd@npm:7.24.7" - dependencies: - "@babel/helper-module-transforms": "npm:^7.24.7" - "@babel/helper-plugin-utils": "npm:^7.24.7" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 7791d290121db210e4338b94b4a069a1a79e4c7a8d7638d8159a97b281851bbed3048dac87a4ae718ad963005e6c14a5d28e6db2eeb2b04e031cee92fb312f85 - languageName: node - linkType: hard - "@babel/plugin-transform-named-capturing-groups-regex@npm:^7.0.0, @babel/plugin-transform-named-capturing-groups-regex@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-named-capturing-groups-regex@npm:7.22.5" @@ -2527,18 +2175,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-named-capturing-groups-regex@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/plugin-transform-named-capturing-groups-regex@npm:7.24.7" - dependencies: - "@babel/helper-create-regexp-features-plugin": "npm:^7.24.7" - "@babel/helper-plugin-utils": "npm:^7.24.7" - peerDependencies: - "@babel/core": ^7.0.0 - checksum: 41a0b0f2d0886318237440aa3b489f6d0305361d8671121777d9ff89f9f6de9d0c02ce93625049061426c8994064ef64deae8b819d1b14c00374a6a2336fb5d9 - languageName: node - linkType: hard - "@babel/plugin-transform-new-target@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-new-target@npm:7.22.5" @@ -2550,17 +2186,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-new-target@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/plugin-transform-new-target@npm:7.24.7" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.24.7" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 2540808a35e1a978e537334c43dab439cf24c93e7beb213a2e71902f6710e60e0184316643790c0a6644e7a8021e52f7ab8165e6b3e2d6651be07bdf517b67df - languageName: node - linkType: hard - "@babel/plugin-transform-nullish-coalescing-operator@npm:^7.22.11": version: 7.22.11 resolution: "@babel/plugin-transform-nullish-coalescing-operator@npm:7.22.11" @@ -2573,15 +2198,15 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-nullish-coalescing-operator@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/plugin-transform-nullish-coalescing-operator@npm:7.24.7" +"@babel/plugin-transform-nullish-coalescing-operator@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-nullish-coalescing-operator@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": "npm:^7.24.7" + "@babel/helper-plugin-utils": "npm:^7.22.5" "@babel/plugin-syntax-nullish-coalescing-operator": "npm:^7.8.3" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 7243c8ff734ed5ef759dd8768773c4b443c12e792727e759a1aec2c7fa2bfdd24f1ecb42e292a7b3d8bd3d7f7b861cf256a8eb4ba144fc9cc463892c303083d9 + checksum: 66f7237d59060954fc0ba0c5d9e7081580421014b446080b3efedb3d4be9a4346f50974c5886a4ec7962db9992e5e1c5e26cb76801728b4d9626ac2eb09c26f7 languageName: node linkType: hard @@ -2597,15 +2222,15 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-numeric-separator@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/plugin-transform-numeric-separator@npm:7.24.7" +"@babel/plugin-transform-numeric-separator@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-numeric-separator@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": "npm:^7.24.7" + "@babel/helper-plugin-utils": "npm:^7.22.5" "@babel/plugin-syntax-numeric-separator": "npm:^7.10.4" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: e18e09ca5a6342645d00ede477731aa6e8714ff357efc9d7cda5934f1703b3b6fb7d3298dce3ce3ba53e9ff1158eab8f1aadc68874cc21a6099d33a1ca457789 + checksum: 921d6ff2165eb782c28a6c06e9eb0dc17400c9476b000a7f8b8dfa95c122c22be4adee7bc15f035a1e4269842b3a68b0a2f20e4437025a6e0fbe16e479a879b8 languageName: node linkType: hard @@ -2638,17 +2263,18 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-object-rest-spread@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/plugin-transform-object-rest-spread@npm:7.24.7" +"@babel/plugin-transform-object-rest-spread@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-object-rest-spread@npm:7.22.5" dependencies: - "@babel/helper-compilation-targets": "npm:^7.24.7" - "@babel/helper-plugin-utils": "npm:^7.24.7" + "@babel/compat-data": "npm:^7.22.5" + "@babel/helper-compilation-targets": "npm:^7.22.5" + "@babel/helper-plugin-utils": "npm:^7.22.5" "@babel/plugin-syntax-object-rest-spread": "npm:^7.8.3" - "@babel/plugin-transform-parameters": "npm:^7.24.7" + "@babel/plugin-transform-parameters": "npm:^7.22.5" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 9ad64bc003f583030f9da50614b485852f8edac93f8faf5d1cd855201a4852f37c5255ae4daf70dd4375bdd4874e16e39b91f680d4668ec219ba05441ce286eb + checksum: ab93b8f84e4ed6629ea258d94b597976598a1990035b4d5178c8d117908a48a36f0f03dd2f4a3375393a23a588ecc7817c099ac88a80f8307475b9a25e4d08e0 languageName: node linkType: hard @@ -2664,18 +2290,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-object-super@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/plugin-transform-object-super@npm:7.24.7" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.24.7" - "@babel/helper-replace-supers": "npm:^7.24.7" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 770cebb4b4e1872c216b17069db9a13b87dfee747d359dc56d9fcdd66e7544f92dc6ab1861a4e7e0528196aaff2444e4f17dc84efd8eaf162d542b4ba0943869 - languageName: node - linkType: hard - "@babel/plugin-transform-optional-catch-binding@npm:^7.22.11": version: 7.22.11 resolution: "@babel/plugin-transform-optional-catch-binding@npm:7.22.11" @@ -2688,45 +2302,45 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-optional-catch-binding@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/plugin-transform-optional-catch-binding@npm:7.24.7" +"@babel/plugin-transform-optional-catch-binding@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-optional-catch-binding@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": "npm:^7.24.7" + "@babel/helper-plugin-utils": "npm:^7.22.5" "@babel/plugin-syntax-optional-catch-binding": "npm:^7.8.3" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 1e2f10a018f7d03b3bde6c0b70d063df8d5dd5209861d4467726cf834f5e3d354e2276079dc226aa8e6ece35f5c9b264d64b8229a8bb232829c01e561bcfb07a + checksum: a15bfa5b36f5f1f61521cc1c73e1e394fbd08aef82a416e2e43f5fc7b43830f17d4c9a5605f1b69ed2bbbacd6f49f5e4f9a3e8e0b7a83841bc95e8ef2116f0a9 languageName: node linkType: hard -"@babel/plugin-transform-optional-chaining@npm:^7.22.15, @babel/plugin-transform-optional-chaining@npm:^7.23.0": - version: 7.23.0 - resolution: "@babel/plugin-transform-optional-chaining@npm:7.23.0" +"@babel/plugin-transform-optional-chaining@npm:^7.22.10, @babel/plugin-transform-optional-chaining@npm:^7.22.5": + version: 7.22.10 + resolution: "@babel/plugin-transform-optional-chaining@npm:7.22.10" dependencies: "@babel/helper-plugin-utils": "npm:^7.22.5" "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.22.5" "@babel/plugin-syntax-optional-chaining": "npm:^7.8.3" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 2bf605b908c75f8d7616e8be52e4656983f2b027032260fbf5279f28297a67a1a28ec3ed60cd5760537dbd08a021246b8092ce06fb2418884390230b807142b3 + checksum: 18ee2fff4f922141a31025445a40834d0af7bb398e39d4ad5825de8a8de54f56585b7db4a4d1c0811ed106b780be26e7626806579387787c473ff4fed77778bb languageName: node linkType: hard -"@babel/plugin-transform-optional-chaining@npm:^7.24.7, @babel/plugin-transform-optional-chaining@npm:^7.24.8": - version: 7.24.8 - resolution: "@babel/plugin-transform-optional-chaining@npm:7.24.8" +"@babel/plugin-transform-optional-chaining@npm:^7.22.15, @babel/plugin-transform-optional-chaining@npm:^7.23.0": + version: 7.23.0 + resolution: "@babel/plugin-transform-optional-chaining@npm:7.23.0" dependencies: - "@babel/helper-plugin-utils": "npm:^7.24.8" - "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.24.7" + "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.22.5" "@babel/plugin-syntax-optional-chaining": "npm:^7.8.3" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 4ffbe1aad7dec7c9aa2bf6ceb4b2f91f96815b2784f2879bde80e46934f59d64a12cb2c6262e40897c4754d77d2c35d8a5cfed63044fdebf94978b1ed3d14b17 + checksum: 2bf605b908c75f8d7616e8be52e4656983f2b027032260fbf5279f28297a67a1a28ec3ed60cd5760537dbd08a021246b8092ce06fb2418884390230b807142b3 languageName: node linkType: hard -"@babel/plugin-transform-parameters@npm:^7.0.0, @babel/plugin-transform-parameters@npm:^7.20.7": +"@babel/plugin-transform-parameters@npm:^7.0.0, @babel/plugin-transform-parameters@npm:^7.20.7, @babel/plugin-transform-parameters@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-parameters@npm:7.22.5" dependencies: @@ -2759,17 +2373,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-parameters@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/plugin-transform-parameters@npm:7.24.7" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.24.7" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 53bf190d6926771545d5184f1f5f3f5144d0f04f170799ad46a43f683a01fab8d5fe4d2196cf246774530990c31fe1f2b9f0def39f0a5ddbb2340b924f5edf01 - languageName: node - linkType: hard - "@babel/plugin-transform-private-methods@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-private-methods@npm:7.22.5" @@ -2782,18 +2385,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-private-methods@npm:^7.25.4": - version: 7.25.4 - resolution: "@babel/plugin-transform-private-methods@npm:7.25.4" - dependencies: - "@babel/helper-create-class-features-plugin": "npm:^7.25.4" - "@babel/helper-plugin-utils": "npm:^7.24.8" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 7abdb427c3984a2c8a2e9d806297d8509b02f78a3501b7760e544be532446e9df328b876daa8fc38718f3dce7ccc45083016ee7aeaab169b81c142bc18700794 - languageName: node - linkType: hard - "@babel/plugin-transform-private-property-in-object@npm:^7.22.11": version: 7.22.11 resolution: "@babel/plugin-transform-private-property-in-object@npm:7.22.11" @@ -2808,17 +2399,17 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-private-property-in-object@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/plugin-transform-private-property-in-object@npm:7.24.7" +"@babel/plugin-transform-private-property-in-object@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-private-property-in-object@npm:7.22.5" dependencies: - "@babel/helper-annotate-as-pure": "npm:^7.24.7" - "@babel/helper-create-class-features-plugin": "npm:^7.24.7" - "@babel/helper-plugin-utils": "npm:^7.24.7" + "@babel/helper-annotate-as-pure": "npm:^7.22.5" + "@babel/helper-create-class-features-plugin": "npm:^7.22.5" + "@babel/helper-plugin-utils": "npm:^7.22.5" "@babel/plugin-syntax-private-property-in-object": "npm:^7.14.5" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: c6fa7defb90b1b0ed46f24ff94ff2e77f44c1f478d1090e81712f33cf992dda5ba347016f030082a2f770138bac6f4a9c2c1565e9f767a125901c77dd9c239ba + checksum: f178191da005d986fdeb30ef74ea0d28878e6225d305d931ce925d87b7df432f5bb29e32173cff2a5c408cee7abc9f25fab09530d4f419ce5cc29a44a89f7a55 languageName: node linkType: hard @@ -2833,17 +2424,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-property-literals@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/plugin-transform-property-literals@npm:7.24.7" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.24.7" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 52564b58f3d111dc02d241d5892a4b01512e98dfdf6ef11b0ed62f8b11b0acacccef0fc229b44114fe8d1a57a8b70780b11bdd18b807d3754a781a07d8f57433 - languageName: node - linkType: hard - "@babel/plugin-transform-react-display-name@npm:^7.0.0, @babel/plugin-transform-react-display-name@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-react-display-name@npm:7.22.5" @@ -2866,17 +2446,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-react-display-name@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/plugin-transform-react-display-name@npm:7.24.7" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.24.7" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: c14a07a9e75723c96f1a0a306b8a8e899ff1c6a0cc3d62bcda79bb1b54e4319127b258651c513a1a47da152cdc22e16525525a30ae5933a2980c7036fd0b4d24 - languageName: node - linkType: hard - "@babel/plugin-transform-react-jsx-development@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-react-jsx-development@npm:7.22.5" @@ -2888,17 +2457,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-react-jsx-development@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/plugin-transform-react-jsx-development@npm:7.24.7" - dependencies: - "@babel/plugin-transform-react-jsx": "npm:^7.24.7" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: fce647db50f90a5291681f0f97865d9dc76981262dff71d6d0332e724b85343de5860c26f9e9a79e448d61e1d70916b07ce91e8c7f2b80dceb4b16aee41794d8 - languageName: node - linkType: hard - "@babel/plugin-transform-react-jsx-self@npm:^7.0.0": version: 7.22.5 resolution: "@babel/plugin-transform-react-jsx-self@npm:7.22.5" @@ -2966,21 +2524,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-react-jsx@npm:^7.24.7": - version: 7.25.2 - resolution: "@babel/plugin-transform-react-jsx@npm:7.25.2" - dependencies: - "@babel/helper-annotate-as-pure": "npm:^7.24.7" - "@babel/helper-module-imports": "npm:^7.24.7" - "@babel/helper-plugin-utils": "npm:^7.24.8" - "@babel/plugin-syntax-jsx": "npm:^7.24.7" - "@babel/types": "npm:^7.25.2" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 8c5b515f38118471197605e02bea54a8a4283010e3c55bad8cfb78de59ad63612b14d40baca63689afdc9d57b147aac4c7794fe5f7736c9e1ed6dd38784be624 - languageName: node - linkType: hard - "@babel/plugin-transform-react-pure-annotations@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-react-pure-annotations@npm:7.22.5" @@ -3005,18 +2548,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-react-pure-annotations@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/plugin-transform-react-pure-annotations@npm:7.24.7" - dependencies: - "@babel/helper-annotate-as-pure": "npm:^7.24.7" - "@babel/helper-plugin-utils": "npm:^7.24.7" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: fae517d293d9c93b7b920458c3e4b91cb0400513889af41ba184a5f3acc8bfef27242cc262741bb8f87870df376f1733a0d0f52b966d342e2aaaf5607af8f73d - languageName: node - linkType: hard - "@babel/plugin-transform-regenerator@npm:^7.22.10": version: 7.22.10 resolution: "@babel/plugin-transform-regenerator@npm:7.22.10" @@ -3029,18 +2560,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-regenerator@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/plugin-transform-regenerator@npm:7.24.7" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.24.7" - regenerator-transform: "npm:^0.15.2" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: d2dc2c788fdae9d97217e70d46ba8ca9db0035c398dc3e161552b0c437113719a75c04f201f9c91ddc8d28a1da60d0b0853f616dead98a396abb9c845c44892b - languageName: node - linkType: hard - "@babel/plugin-transform-reserved-words@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-reserved-words@npm:7.22.5" @@ -3052,17 +2571,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-reserved-words@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/plugin-transform-reserved-words@npm:7.24.7" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.24.7" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 2229de2768615e7f5dc0bbc55bc121b5678fd6d2febd46c74a58e42bb894d74cd5955c805880f4e02d0e1cf94f6886270eda7fafc1be9305a1ec3b9fd1d063f5 - languageName: node - linkType: hard - "@babel/plugin-transform-runtime@npm:^7.0.0": version: 7.22.10 resolution: "@babel/plugin-transform-runtime@npm:7.22.10" @@ -3083,21 +2591,10 @@ __metadata: version: 7.22.5 resolution: "@babel/plugin-transform-shorthand-properties@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": "npm:^7.22.5" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: d2dd6b7033f536dd74569d7343bf3ca88c4bc12575e572a2c5446f42a1ebc8e69cec5e38fc0e63ac7c4a48b944a3225e4317d5db94287b9a5b381a5045c0cdb2 - languageName: node - linkType: hard - -"@babel/plugin-transform-shorthand-properties@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/plugin-transform-shorthand-properties@npm:7.24.7" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.24.7" + "@babel/helper-plugin-utils": "npm:^7.22.5" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 41b155bdbb3be66618358488bf7731b3b2e8fff2de3dbfd541847720a9debfcec14db06a117abedd03c9cd786db20a79e2a86509a4f19513f6e1b610520905cf + checksum: d2dd6b7033f536dd74569d7343bf3ca88c4bc12575e572a2c5446f42a1ebc8e69cec5e38fc0e63ac7c4a48b944a3225e4317d5db94287b9a5b381a5045c0cdb2 languageName: node linkType: hard @@ -3113,18 +2610,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-spread@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/plugin-transform-spread@npm:7.24.7" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.24.7" - "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.24.7" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: facba1553035f76b0d2930d4ada89a8cd0f45b79579afd35baefbfaf12e3b86096995f4b0c402cf9ee23b3f2ea0a4460c3b1ec0c192d340962c948bb223d4e66 - languageName: node - linkType: hard - "@babel/plugin-transform-sticky-regex@npm:^7.0.0, @babel/plugin-transform-sticky-regex@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-sticky-regex@npm:7.22.5" @@ -3136,17 +2621,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-sticky-regex@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/plugin-transform-sticky-regex@npm:7.24.7" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.24.7" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 5a74ed2ed0a3ab51c3d15fcaf09d9e2fe915823535c7a4d7b019813177d559b69677090e189ec3d5d08b619483eb5ad371fbcfbbff5ace2a76ba33ee566a1109 - languageName: node - linkType: hard - "@babel/plugin-transform-template-literals@npm:^7.0.0, @babel/plugin-transform-template-literals@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-template-literals@npm:7.22.5" @@ -3158,17 +2632,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-template-literals@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/plugin-transform-template-literals@npm:7.24.7" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.24.7" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 3630f966257bcace122f04d3157416a09d40768c44c3a800855da81146b009187daa21859d1c3b7d13f4e19e8888e60613964b175b2275d451200fb6d8d6cfe6 - languageName: node - linkType: hard - "@babel/plugin-transform-typeof-symbol@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-typeof-symbol@npm:7.22.5" @@ -3180,17 +2643,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-typeof-symbol@npm:^7.24.8": - version: 7.24.8 - resolution: "@babel/plugin-transform-typeof-symbol@npm:7.24.8" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.24.8" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 2f570a4fbbdc5fd85f48165a97452826560051e3b8efb48c3bb0a0a33ee8485633439e7b71bfe3ef705583a1df43f854f49125bd759abdedc195b2cf7e60012a - languageName: node - linkType: hard - "@babel/plugin-transform-typescript@npm:^7.22.15": version: 7.22.15 resolution: "@babel/plugin-transform-typescript@npm:7.22.15" @@ -3244,17 +2696,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-unicode-escapes@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/plugin-transform-unicode-escapes@npm:7.24.7" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.24.7" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 8b18e2e66af33471a6971289492beff5c240e56727331db1d34c4338a6a368a82a7ed6d57ec911001b6d65643aed76531e1e7cac93265fb3fb2717f54d845e69 - languageName: node - linkType: hard - "@babel/plugin-transform-unicode-property-regex@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-unicode-property-regex@npm:7.22.5" @@ -3267,18 +2708,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-unicode-property-regex@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/plugin-transform-unicode-property-regex@npm:7.24.7" - dependencies: - "@babel/helper-create-regexp-features-plugin": "npm:^7.24.7" - "@babel/helper-plugin-utils": "npm:^7.24.7" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: bc57656eb94584d1b74a385d378818ac2b3fca642e3f649fead8da5fb3f9de22f8461185936915dfb33d5a9104e62e7a47828331248b09d28bb2d59e9276de3e - languageName: node - linkType: hard - "@babel/plugin-transform-unicode-regex@npm:^7.0.0, @babel/plugin-transform-unicode-regex@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-unicode-regex@npm:7.22.5" @@ -3291,18 +2720,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-unicode-regex@npm:^7.24.7": - version: 7.24.7 - resolution: "@babel/plugin-transform-unicode-regex@npm:7.24.7" - dependencies: - "@babel/helper-create-regexp-features-plugin": "npm:^7.24.7" - "@babel/helper-plugin-utils": "npm:^7.24.7" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 83f72a345b751566b601dc4d07e9f2c8f1bc0e0c6f7abb56ceb3095b3c9d304de73f85f2f477a09f8cc7edd5e65afd0ff9e376cdbcbea33bc0c28f3705b38fd9 - languageName: node - linkType: hard - "@babel/plugin-transform-unicode-sets-regex@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-unicode-sets-regex@npm:7.22.5" @@ -3315,18 +2732,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-unicode-sets-regex@npm:^7.25.4": - version: 7.25.4 - resolution: "@babel/plugin-transform-unicode-sets-regex@npm:7.25.4" - dependencies: - "@babel/helper-create-regexp-features-plugin": "npm:^7.25.2" - "@babel/helper-plugin-utils": "npm:^7.24.8" - peerDependencies: - "@babel/core": ^7.0.0 - checksum: f65749835a98d8d6242e961f9276bdcdb09020e791d151ccc145acaca9a66f025b2c7cb761104f139180d35eb066a429596ee6edece81f5fd9244e0edb97d7ec - languageName: node - linkType: hard - "@babel/preset-env@npm:^7.18.2": version: 7.23.2 resolution: "@babel/preset-env@npm:7.23.2" @@ -3418,26 +2823,23 @@ __metadata: linkType: hard "@babel/preset-env@npm:^7.22.10": - version: 7.25.4 - resolution: "@babel/preset-env@npm:7.25.4" + version: 7.22.10 + resolution: "@babel/preset-env@npm:7.22.10" dependencies: - "@babel/compat-data": "npm:^7.25.4" - "@babel/helper-compilation-targets": "npm:^7.25.2" - "@babel/helper-plugin-utils": "npm:^7.24.8" - "@babel/helper-validator-option": "npm:^7.24.8" - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "npm:^7.25.3" - "@babel/plugin-bugfix-safari-class-field-initializer-scope": "npm:^7.25.0" - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "npm:^7.25.0" - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "npm:^7.24.7" - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "npm:^7.25.0" + "@babel/compat-data": "npm:^7.22.9" + "@babel/helper-compilation-targets": "npm:^7.22.10" + "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-validator-option": "npm:^7.22.5" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "npm:^7.22.5" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "npm:^7.22.5" "@babel/plugin-proposal-private-property-in-object": "npm:7.21.0-placeholder-for-preset-env.2" "@babel/plugin-syntax-async-generators": "npm:^7.8.4" "@babel/plugin-syntax-class-properties": "npm:^7.12.13" "@babel/plugin-syntax-class-static-block": "npm:^7.14.5" "@babel/plugin-syntax-dynamic-import": "npm:^7.8.3" "@babel/plugin-syntax-export-namespace-from": "npm:^7.8.3" - "@babel/plugin-syntax-import-assertions": "npm:^7.24.7" - "@babel/plugin-syntax-import-attributes": "npm:^7.24.7" + "@babel/plugin-syntax-import-assertions": "npm:^7.22.5" + "@babel/plugin-syntax-import-attributes": "npm:^7.22.5" "@babel/plugin-syntax-import-meta": "npm:^7.10.4" "@babel/plugin-syntax-json-strings": "npm:^7.8.3" "@babel/plugin-syntax-logical-assignment-operators": "npm:^7.10.4" @@ -3449,64 +2851,64 @@ __metadata: "@babel/plugin-syntax-private-property-in-object": "npm:^7.14.5" "@babel/plugin-syntax-top-level-await": "npm:^7.14.5" "@babel/plugin-syntax-unicode-sets-regex": "npm:^7.18.6" - "@babel/plugin-transform-arrow-functions": "npm:^7.24.7" - "@babel/plugin-transform-async-generator-functions": "npm:^7.25.4" - "@babel/plugin-transform-async-to-generator": "npm:^7.24.7" - "@babel/plugin-transform-block-scoped-functions": "npm:^7.24.7" - "@babel/plugin-transform-block-scoping": "npm:^7.25.0" - "@babel/plugin-transform-class-properties": "npm:^7.25.4" - "@babel/plugin-transform-class-static-block": "npm:^7.24.7" - "@babel/plugin-transform-classes": "npm:^7.25.4" - "@babel/plugin-transform-computed-properties": "npm:^7.24.7" - "@babel/plugin-transform-destructuring": "npm:^7.24.8" - "@babel/plugin-transform-dotall-regex": "npm:^7.24.7" - "@babel/plugin-transform-duplicate-keys": "npm:^7.24.7" - "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "npm:^7.25.0" - "@babel/plugin-transform-dynamic-import": "npm:^7.24.7" - "@babel/plugin-transform-exponentiation-operator": "npm:^7.24.7" - "@babel/plugin-transform-export-namespace-from": "npm:^7.24.7" - "@babel/plugin-transform-for-of": "npm:^7.24.7" - "@babel/plugin-transform-function-name": "npm:^7.25.1" - "@babel/plugin-transform-json-strings": "npm:^7.24.7" - "@babel/plugin-transform-literals": "npm:^7.25.2" - "@babel/plugin-transform-logical-assignment-operators": "npm:^7.24.7" - "@babel/plugin-transform-member-expression-literals": "npm:^7.24.7" - "@babel/plugin-transform-modules-amd": "npm:^7.24.7" - "@babel/plugin-transform-modules-commonjs": "npm:^7.24.8" - "@babel/plugin-transform-modules-systemjs": "npm:^7.25.0" - "@babel/plugin-transform-modules-umd": "npm:^7.24.7" - "@babel/plugin-transform-named-capturing-groups-regex": "npm:^7.24.7" - "@babel/plugin-transform-new-target": "npm:^7.24.7" - "@babel/plugin-transform-nullish-coalescing-operator": "npm:^7.24.7" - "@babel/plugin-transform-numeric-separator": "npm:^7.24.7" - "@babel/plugin-transform-object-rest-spread": "npm:^7.24.7" - "@babel/plugin-transform-object-super": "npm:^7.24.7" - "@babel/plugin-transform-optional-catch-binding": "npm:^7.24.7" - "@babel/plugin-transform-optional-chaining": "npm:^7.24.8" - "@babel/plugin-transform-parameters": "npm:^7.24.7" - "@babel/plugin-transform-private-methods": "npm:^7.25.4" - "@babel/plugin-transform-private-property-in-object": "npm:^7.24.7" - "@babel/plugin-transform-property-literals": "npm:^7.24.7" - "@babel/plugin-transform-regenerator": "npm:^7.24.7" - "@babel/plugin-transform-reserved-words": "npm:^7.24.7" - "@babel/plugin-transform-shorthand-properties": "npm:^7.24.7" - "@babel/plugin-transform-spread": "npm:^7.24.7" - "@babel/plugin-transform-sticky-regex": "npm:^7.24.7" - "@babel/plugin-transform-template-literals": "npm:^7.24.7" - "@babel/plugin-transform-typeof-symbol": "npm:^7.24.8" - "@babel/plugin-transform-unicode-escapes": "npm:^7.24.7" - "@babel/plugin-transform-unicode-property-regex": "npm:^7.24.7" - "@babel/plugin-transform-unicode-regex": "npm:^7.24.7" - "@babel/plugin-transform-unicode-sets-regex": "npm:^7.25.4" + "@babel/plugin-transform-arrow-functions": "npm:^7.22.5" + "@babel/plugin-transform-async-generator-functions": "npm:^7.22.10" + "@babel/plugin-transform-async-to-generator": "npm:^7.22.5" + "@babel/plugin-transform-block-scoped-functions": "npm:^7.22.5" + "@babel/plugin-transform-block-scoping": "npm:^7.22.10" + "@babel/plugin-transform-class-properties": "npm:^7.22.5" + "@babel/plugin-transform-class-static-block": "npm:^7.22.5" + "@babel/plugin-transform-classes": "npm:^7.22.6" + "@babel/plugin-transform-computed-properties": "npm:^7.22.5" + "@babel/plugin-transform-destructuring": "npm:^7.22.10" + "@babel/plugin-transform-dotall-regex": "npm:^7.22.5" + "@babel/plugin-transform-duplicate-keys": "npm:^7.22.5" + "@babel/plugin-transform-dynamic-import": "npm:^7.22.5" + "@babel/plugin-transform-exponentiation-operator": "npm:^7.22.5" + "@babel/plugin-transform-export-namespace-from": "npm:^7.22.5" + "@babel/plugin-transform-for-of": "npm:^7.22.5" + "@babel/plugin-transform-function-name": "npm:^7.22.5" + "@babel/plugin-transform-json-strings": "npm:^7.22.5" + "@babel/plugin-transform-literals": "npm:^7.22.5" + "@babel/plugin-transform-logical-assignment-operators": "npm:^7.22.5" + "@babel/plugin-transform-member-expression-literals": "npm:^7.22.5" + "@babel/plugin-transform-modules-amd": "npm:^7.22.5" + "@babel/plugin-transform-modules-commonjs": "npm:^7.22.5" + "@babel/plugin-transform-modules-systemjs": "npm:^7.22.5" + "@babel/plugin-transform-modules-umd": "npm:^7.22.5" + "@babel/plugin-transform-named-capturing-groups-regex": "npm:^7.22.5" + "@babel/plugin-transform-new-target": "npm:^7.22.5" + "@babel/plugin-transform-nullish-coalescing-operator": "npm:^7.22.5" + "@babel/plugin-transform-numeric-separator": "npm:^7.22.5" + "@babel/plugin-transform-object-rest-spread": "npm:^7.22.5" + "@babel/plugin-transform-object-super": "npm:^7.22.5" + "@babel/plugin-transform-optional-catch-binding": "npm:^7.22.5" + "@babel/plugin-transform-optional-chaining": "npm:^7.22.10" + "@babel/plugin-transform-parameters": "npm:^7.22.5" + "@babel/plugin-transform-private-methods": "npm:^7.22.5" + "@babel/plugin-transform-private-property-in-object": "npm:^7.22.5" + "@babel/plugin-transform-property-literals": "npm:^7.22.5" + "@babel/plugin-transform-regenerator": "npm:^7.22.10" + "@babel/plugin-transform-reserved-words": "npm:^7.22.5" + "@babel/plugin-transform-shorthand-properties": "npm:^7.22.5" + "@babel/plugin-transform-spread": "npm:^7.22.5" + "@babel/plugin-transform-sticky-regex": "npm:^7.22.5" + "@babel/plugin-transform-template-literals": "npm:^7.22.5" + "@babel/plugin-transform-typeof-symbol": "npm:^7.22.5" + "@babel/plugin-transform-unicode-escapes": "npm:^7.22.10" + "@babel/plugin-transform-unicode-property-regex": "npm:^7.22.5" + "@babel/plugin-transform-unicode-regex": "npm:^7.22.5" + "@babel/plugin-transform-unicode-sets-regex": "npm:^7.22.5" "@babel/preset-modules": "npm:0.1.6-no-external-plugins" - babel-plugin-polyfill-corejs2: "npm:^0.4.10" - babel-plugin-polyfill-corejs3: "npm:^0.10.6" - babel-plugin-polyfill-regenerator: "npm:^0.6.1" - core-js-compat: "npm:^3.37.1" + "@babel/types": "npm:^7.22.10" + babel-plugin-polyfill-corejs2: "npm:^0.4.5" + babel-plugin-polyfill-corejs3: "npm:^0.8.3" + babel-plugin-polyfill-regenerator: "npm:^0.5.2" + core-js-compat: "npm:^3.31.0" semver: "npm:^6.3.1" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: ed210a1974b5a1e7f80a933c87253907ec869457cea900bc97892642fa9a690c47627a9bac08a7c9495deb992a2b15f308ffca2741e1876ba47172c96fa27e14 + checksum: 56552a5298e4bdb89a075f88638e3dfb4937e9e781ba682a1a4c9c68551b6471ed79e5d85d8d006421645e8c9ff500f18efb341d76cead5f110aefb6bdbac098 languageName: node linkType: hard @@ -3582,18 +2984,18 @@ __metadata: linkType: hard "@babel/preset-react@npm:^7.22.5": - version: 7.24.7 - resolution: "@babel/preset-react@npm:7.24.7" + version: 7.22.5 + resolution: "@babel/preset-react@npm:7.22.5" dependencies: - "@babel/helper-plugin-utils": "npm:^7.24.7" - "@babel/helper-validator-option": "npm:^7.24.7" - "@babel/plugin-transform-react-display-name": "npm:^7.24.7" - "@babel/plugin-transform-react-jsx": "npm:^7.24.7" - "@babel/plugin-transform-react-jsx-development": "npm:^7.24.7" - "@babel/plugin-transform-react-pure-annotations": "npm:^7.24.7" + "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/helper-validator-option": "npm:^7.22.5" + "@babel/plugin-transform-react-display-name": "npm:^7.22.5" + "@babel/plugin-transform-react-jsx": "npm:^7.22.5" + "@babel/plugin-transform-react-jsx-development": "npm:^7.22.5" + "@babel/plugin-transform-react-pure-annotations": "npm:^7.22.5" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 9658b685b25cedaadd0b65c4e663fbc7f57394b5036ddb4c99b1a75b0711fb83292c1c625d605c05b73413fc7a6dc20e532627f6a39b6dc8d4e00415479b054c + checksum: 60c1fde93d5a6bda03b3d2bb61bcbf056925fd0b01e84d789eaf2a06f639d8714e93735a75da0221fd7a8407c6b4fea7b4fbc35de5ff5d5a299aecb1c82fd530 languageName: node linkType: hard @@ -3744,7 +3146,7 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:^7.18.9, @babel/traverse@npm:^7.25.0, @babel/traverse@npm:^7.25.1, @babel/traverse@npm:^7.25.2, @babel/traverse@npm:^7.25.3, @babel/traverse@npm:^7.25.4": +"@babel/traverse@npm:^7.18.9, @babel/traverse@npm:^7.25.2": version: 7.25.6 resolution: "@babel/traverse@npm:7.25.6" dependencies: @@ -4421,15 +3823,15 @@ __metadata: linkType: hard "@eslint-community/regexpp@npm:^4.6.1": - version: 4.11.1 - resolution: "@eslint-community/regexpp@npm:4.11.1" - checksum: fbcc1cb65ef5ed5b92faa8dc542e035269065e7ebcc0b39c81a4fe98ad35cfff20b3c8df048641de15a7757e07d69f85e2579c1a5055f993413ba18c055654f8 + version: 4.6.2 + resolution: "@eslint-community/regexpp@npm:4.6.2" + checksum: da800788298f8419f4c4e04eaa4e3c97e7f57537e822e7b150de662e420e3d437816b863e490807bd0b00e715b0989f9d8864bf54357cbcfa84e4255b910789d languageName: node linkType: hard -"@eslint/eslintrc@npm:^2.1.4": - version: 2.1.4 - resolution: "@eslint/eslintrc@npm:2.1.4" +"@eslint/eslintrc@npm:^2.1.1": + version: 2.1.1 + resolution: "@eslint/eslintrc@npm:2.1.1" dependencies: ajv: "npm:^6.12.4" debug: "npm:^4.3.2" @@ -4440,14 +3842,14 @@ __metadata: js-yaml: "npm:^4.1.0" minimatch: "npm:^3.1.2" strip-json-comments: "npm:^3.1.1" - checksum: 32f67052b81768ae876c84569ffd562491ec5a5091b0c1e1ca1e0f3c24fb42f804952fdd0a137873bc64303ba368a71ba079a6f691cee25beee9722d94cc8573 + checksum: 104ec8997206eabc87de84b87a2852efce0ff98730d377061734da2554c79c9b6d417fbe66248ef5566a0501ef41fddec3a00f79b77731102903586a63b2ed34 languageName: node linkType: hard -"@eslint/js@npm:8.57.1": - version: 8.57.1 - resolution: "@eslint/js@npm:8.57.1" - checksum: b489c474a3b5b54381c62e82b3f7f65f4b8a5eaaed126546520bf2fede5532a8ed53212919fed1e9048dcf7f37167c8561d58d0ba4492a4244004e7793805223 +"@eslint/js@npm:^8.46.0": + version: 8.46.0 + resolution: "@eslint/js@npm:8.46.0" + checksum: 674c5800e4e9829322aa84195b23c59db326cb42190ac0284bdfe70b2442d544837f3006d8d8c166afaa86ab7072df1b77f7fdb43a60aa2bb1ede90d82e38540 languageName: node linkType: hard @@ -5340,14 +4742,14 @@ __metadata: languageName: node linkType: hard -"@humanwhocodes/config-array@npm:^0.13.0": - version: 0.13.0 - resolution: "@humanwhocodes/config-array@npm:0.13.0" +"@humanwhocodes/config-array@npm:^0.11.10": + version: 0.11.10 + resolution: "@humanwhocodes/config-array@npm:0.11.10" dependencies: - "@humanwhocodes/object-schema": "npm:^2.0.3" - debug: "npm:^4.3.1" + "@humanwhocodes/object-schema": "npm:^1.2.1" + debug: "npm:^4.1.1" minimatch: "npm:^3.0.5" - checksum: 205c99e756b759f92e1f44a3dc6292b37db199beacba8f26c2165d4051fe73a4ae52fdcfd08ffa93e7e5cb63da7c88648f0e84e197d154bbbbe137b2e0dd332e + checksum: 9e307a49a5baa28beb243d2c14c145f288fccd6885f4c92a9055707057ec40980242256b2a07c976cfa6c75f7081da111a40a9844d1ca8daeff2302f8b640e76 languageName: node linkType: hard @@ -5358,10 +4760,10 @@ __metadata: languageName: node linkType: hard -"@humanwhocodes/object-schema@npm:^2.0.3": - version: 2.0.3 - resolution: "@humanwhocodes/object-schema@npm:2.0.3" - checksum: 80520eabbfc2d32fe195a93557cef50dfe8c8905de447f022675aaf66abc33ae54098f5ea78548d925aa671cd4ab7c7daa5ad704fe42358c9b5e7db60f80696c +"@humanwhocodes/object-schema@npm:^1.2.1": + version: 1.2.1 + resolution: "@humanwhocodes/object-schema@npm:1.2.1" + checksum: c3c35fdb70c04a569278351c75553e293ae339684ed75895edc79facc7276e351115786946658d78133130c0cca80e57e2203bc07f8fa7fe7980300e8deef7db languageName: node linkType: hard @@ -6464,10 +5866,17 @@ __metadata: languageName: node linkType: hard -"@pkgr/core@npm:^0.1.0": - version: 0.1.1 - resolution: "@pkgr/core@npm:0.1.1" - checksum: 3f7536bc7f57320ab2cf96f8973664bef624710c403357429fbf680a5c3b4843c1dbd389bb43daa6b1f6f1f007bb082f5abcb76bb2b5dc9f421647743b71d3d8 +"@pkgr/utils@npm:^2.3.1": + version: 2.4.2 + resolution: "@pkgr/utils@npm:2.4.2" + dependencies: + cross-spawn: "npm:^7.0.3" + fast-glob: "npm:^3.3.0" + is-glob: "npm:^4.0.3" + open: "npm:^9.1.0" + picocolors: "npm:^1.0.0" + tslib: "npm:^2.6.0" + checksum: 7c3e68f6405a1d4c51f418d8d580e71d7bade2683d5db07e8413d8e57f7e389047eda44a2341f77a1b3085895fca7676a9d45e8812a58312524f8c4c65d501be languageName: node linkType: hard @@ -7349,6 +6758,9 @@ __metadata: "@reown/appkit-common-react-native@npm:1.0.0, @reown/appkit-common-react-native@workspace:packages/common": version: 0.0.0-use.local resolution: "@reown/appkit-common-react-native@workspace:packages/common" + dependencies: + bignumber.js: "npm:9.1.2" + dayjs: "npm:1.11.10" languageName: unknown linkType: soft @@ -9284,7 +8696,7 @@ __metadata: languageName: node linkType: hard -"@ungap/structured-clone@npm:^1.0.0, @ungap/structured-clone@npm:^1.2.0": +"@ungap/structured-clone@npm:^1.0.0": version: 1.2.0 resolution: "@ungap/structured-clone@npm:1.2.0" checksum: 8209c937cb39119f44eb63cf90c0b73e7c754209a6411c707be08e50e29ee81356dca1a848a405c8bdeebfe2f5e4f831ad310ae1689eeef65e7445c090c6657d @@ -10617,19 +10029,6 @@ __metadata: languageName: node linkType: hard -"babel-plugin-polyfill-corejs2@npm:^0.4.10": - version: 0.4.11 - resolution: "babel-plugin-polyfill-corejs2@npm:0.4.11" - dependencies: - "@babel/compat-data": "npm:^7.22.6" - "@babel/helper-define-polyfill-provider": "npm:^0.6.2" - semver: "npm:^6.3.1" - peerDependencies: - "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 - checksum: b2217bc8d5976cf8142453ed44daabf0b2e0e75518f24eac83b54a8892e87a88f1bd9089daa92fd25df979ecd0acfd29b6bc28c4182c1c46344cee15ef9bce84 - languageName: node - linkType: hard - "babel-plugin-polyfill-corejs2@npm:^0.4.5": version: 0.4.5 resolution: "babel-plugin-polyfill-corejs2@npm:0.4.5" @@ -10656,18 +10055,6 @@ __metadata: languageName: node linkType: hard -"babel-plugin-polyfill-corejs3@npm:^0.10.6": - version: 0.10.6 - resolution: "babel-plugin-polyfill-corejs3@npm:0.10.6" - dependencies: - "@babel/helper-define-polyfill-provider": "npm:^0.6.2" - core-js-compat: "npm:^3.38.0" - peerDependencies: - "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 - checksum: 3a69220471b07722c2ae6537310bf26b772514e12b601398082965459c838be70a0ca70b0662f0737070654ff6207673391221d48599abb4a2b27765206d9f79 - languageName: node - linkType: hard - "babel-plugin-polyfill-corejs3@npm:^0.8.3": version: 0.8.3 resolution: "babel-plugin-polyfill-corejs3@npm:0.8.3" @@ -10714,17 +10101,6 @@ __metadata: languageName: node linkType: hard -"babel-plugin-polyfill-regenerator@npm:^0.6.1": - version: 0.6.2 - resolution: "babel-plugin-polyfill-regenerator@npm:0.6.2" - dependencies: - "@babel/helper-define-polyfill-provider": "npm:^0.6.2" - peerDependencies: - "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 - checksum: bc541037cf7620bc84ddb75a1c0ce3288f90e7d2799c070a53f8a495c8c8ae0316447becb06f958dd25dcce2a2fce855d318ecfa48036a1ddb218d55aa38a744 - languageName: node - linkType: hard - "babel-plugin-react-compiler@npm:^0.0.0-experimental-592953e-20240517": version: 0.0.0 resolution: "babel-plugin-react-compiler@npm:0.0.0" @@ -10897,13 +10273,20 @@ __metadata: languageName: node linkType: hard -"big-integer@npm:1.6.x": +"big-integer@npm:1.6.x, big-integer@npm:^1.6.44": version: 1.6.51 resolution: "big-integer@npm:1.6.51" checksum: c8139662d57f8833a44802f4b65be911679c569535ea73c5cfd3c1c8994eaead1b84b6f63e1db63833e4d4cacb6b6a9e5522178113dfdc8e4c81ed8436f1e8cc languageName: node linkType: hard +"bignumber.js@npm:9.1.2": + version: 9.1.2 + resolution: "bignumber.js@npm:9.1.2" + checksum: e17786545433f3110b868725c449fa9625366a6e675cd70eb39b60938d6adbd0158cb4b3ad4f306ce817165d37e63f4aa3098ba4110db1d9a3b9f66abfbaf10d + languageName: node + linkType: hard + "binary-extensions@npm:^2.0.0": version: 2.2.0 resolution: "binary-extensions@npm:2.2.0" @@ -10997,6 +10380,15 @@ __metadata: languageName: node linkType: hard +"bplist-parser@npm:^0.2.0": + version: 0.2.0 + resolution: "bplist-parser@npm:0.2.0" + dependencies: + big-integer: "npm:^1.6.44" + checksum: ce79c69e0f6efe506281e7c84e3712f7d12978991675b6e3a58a295b16f13ca81aa9b845c335614a545e0af728c8311b6aa3142af76ba1cb616af9bbac5c4a9f + languageName: node + linkType: hard + "bplist-parser@npm:^0.3.1": version: 0.3.2 resolution: "bplist-parser@npm:0.3.2" @@ -11104,20 +10496,6 @@ __metadata: languageName: node linkType: hard -"browserslist@npm:^4.23.3": - version: 4.23.3 - resolution: "browserslist@npm:4.23.3" - dependencies: - caniuse-lite: "npm:^1.0.30001646" - electron-to-chromium: "npm:^1.5.4" - node-releases: "npm:^2.0.18" - update-browserslist-db: "npm:^1.1.0" - bin: - browserslist: cli.js - checksum: 3063bfdf812815346447f4796c8f04601bf5d62003374305fd323c2a463e42776475bcc5309264e39bcf9a8605851e53560695991a623be988138b3ff8c66642 - languageName: node - linkType: hard - "bs-logger@npm:0.x": version: 0.2.6 resolution: "bs-logger@npm:0.2.6" @@ -11204,6 +10582,15 @@ __metadata: languageName: node linkType: hard +"bundle-name@npm:^3.0.0": + version: 3.0.0 + resolution: "bundle-name@npm:3.0.0" + dependencies: + run-applescript: "npm:^5.0.0" + checksum: 57bc7f8b025d83961b04db2f1eff6a87f2363c2891f3542a4b82471ff8ebb5d484af48e9784fcdb28ef1d48bb01f03d891966dc3ef58758e46ea32d750ce40f8 + languageName: node + linkType: hard + "bytes@npm:3.0.0": version: 3.0.0 resolution: "bytes@npm:3.0.0" @@ -11351,13 +10738,6 @@ __metadata: languageName: node linkType: hard -"caniuse-lite@npm:^1.0.30001646": - version: 1.0.30001660 - resolution: "caniuse-lite@npm:1.0.30001660" - checksum: d28900b56c597176d515c3175ca75c454f2d30cb2c09a44d7bdb009bb0c4d8a2557905adb77642889bbe9feb85fbfe9d974c8b8e53521fb4b50ee16ab246104e - languageName: node - linkType: hard - "case-sensitive-paths-webpack-plugin@npm:^2.4.0": version: 2.4.0 resolution: "case-sensitive-paths-webpack-plugin@npm:2.4.0" @@ -11949,15 +11329,6 @@ __metadata: languageName: node linkType: hard -"core-js-compat@npm:^3.37.1, core-js-compat@npm:^3.38.0": - version: 3.38.1 - resolution: "core-js-compat@npm:3.38.1" - dependencies: - browserslist: "npm:^4.23.3" - checksum: d8bc8a35591fc5fbf3e376d793f298ec41eb452619c7ef9de4ea59b74be06e9fda799e0dcbf9ba59880dae87e3b41fb191d744ffc988315642a1272bb9442b31 - languageName: node - linkType: hard - "core-util-is@npm:~1.0.0": version: 1.0.3 resolution: "core-util-is@npm:1.0.3" @@ -12213,6 +11584,13 @@ __metadata: languageName: node linkType: hard +"dayjs@npm:1.11.10": + version: 1.11.10 + resolution: "dayjs@npm:1.11.10" + checksum: 4de9af50639d47df87f2e15fa36bb07e0f9ed1e9c52c6caa1482788ee9a384d668f1dbd00c54f82aaab163db07d61d2899384b8254da3a9184fc6deca080e2fe + languageName: node + linkType: hard + "dayjs@npm:^1.8.15": version: 1.11.9 resolution: "dayjs@npm:1.11.9" @@ -12311,6 +11689,28 @@ __metadata: languageName: node linkType: hard +"default-browser-id@npm:^3.0.0": + version: 3.0.0 + resolution: "default-browser-id@npm:3.0.0" + dependencies: + bplist-parser: "npm:^0.2.0" + untildify: "npm:^4.0.0" + checksum: 8db3ab882eb3e1e8b59d84c8641320e6c66d8eeb17eb4bb848b7dd549b1e6fd313988e4a13542e95fbaeff03f6e9dedc5ad191ad4df7996187753eb0d45c00b7 + languageName: node + linkType: hard + +"default-browser@npm:^4.0.0": + version: 4.0.0 + resolution: "default-browser@npm:4.0.0" + dependencies: + bundle-name: "npm:^3.0.0" + default-browser-id: "npm:^3.0.0" + execa: "npm:^7.1.1" + titleize: "npm:^3.0.0" + checksum: 7c8848badc139ecf9d878e562bc4e7ab4301e51ba120b24d8dcb14739c30152115cc612065ac3ab73c02aace4afa29db5a044257b2f0cf234f16e3a58f6c925e + languageName: node + linkType: hard + "default-gateway@npm:^4.2.0": version: 4.2.0 resolution: "default-gateway@npm:4.2.0" @@ -12348,6 +11748,13 @@ __metadata: languageName: node linkType: hard +"define-lazy-prop@npm:^3.0.0": + version: 3.0.0 + resolution: "define-lazy-prop@npm:3.0.0" + checksum: 5ab0b2bf3fa58b3a443140bbd4cd3db1f91b985cc8a246d330b9ac3fc0b6a325a6d82bddc0b055123d745b3f9931afeea74a5ec545439a1630b9c8512b0eeb49 + languageName: node + linkType: hard + "define-properties@npm:^1.1.3, define-properties@npm:^1.1.4, define-properties@npm:^1.2.0": version: 1.2.0 resolution: "define-properties@npm:1.2.0" @@ -12716,13 +12123,6 @@ __metadata: languageName: node linkType: hard -"electron-to-chromium@npm:^1.5.4": - version: 1.5.22 - resolution: "electron-to-chromium@npm:1.5.22" - checksum: 3c1c640dfa77e9d8e16c112d79ddbe87b47b2df7fada2406f2974b22227dd4592a5215c3318baf570ffdc1479151589dbdb8c0eac61347e9c78e1710f3b7ee5d - languageName: node - linkType: hard - "elliptic@npm:6.5.4": version: 6.5.4 resolution: "elliptic@npm:6.5.4" @@ -13395,7 +12795,7 @@ __metadata: languageName: node linkType: hard -"eslint-visitor-keys@npm:^3.3.0": +"eslint-visitor-keys@npm:^3.3.0, eslint-visitor-keys@npm:^3.4.2": version: 3.4.2 resolution: "eslint-visitor-keys@npm:3.4.2" checksum: 4521d1d470490c89fb613aec6fb2f0814b496a4618619ec8dfcc985640fe33c9c64f3dab882f50ebb401b4613f35f2601a9ef9a72b57739af5b0150fecdaf1f1 @@ -13410,17 +12810,16 @@ __metadata: linkType: hard "eslint@npm:^8.46.0": - version: 8.57.1 - resolution: "eslint@npm:8.57.1" + version: 8.46.0 + resolution: "eslint@npm:8.46.0" dependencies: "@eslint-community/eslint-utils": "npm:^4.2.0" "@eslint-community/regexpp": "npm:^4.6.1" - "@eslint/eslintrc": "npm:^2.1.4" - "@eslint/js": "npm:8.57.1" - "@humanwhocodes/config-array": "npm:^0.13.0" + "@eslint/eslintrc": "npm:^2.1.1" + "@eslint/js": "npm:^8.46.0" + "@humanwhocodes/config-array": "npm:^0.11.10" "@humanwhocodes/module-importer": "npm:^1.0.1" "@nodelib/fs.walk": "npm:^1.2.8" - "@ungap/structured-clone": "npm:^1.2.0" ajv: "npm:^6.12.4" chalk: "npm:^4.0.0" cross-spawn: "npm:^7.0.2" @@ -13428,7 +12827,7 @@ __metadata: doctrine: "npm:^3.0.0" escape-string-regexp: "npm:^4.0.0" eslint-scope: "npm:^7.2.2" - eslint-visitor-keys: "npm:^3.4.3" + eslint-visitor-keys: "npm:^3.4.2" espree: "npm:^9.6.1" esquery: "npm:^1.4.2" esutils: "npm:^2.0.2" @@ -13453,7 +12852,7 @@ __metadata: text-table: "npm:^0.2.0" bin: eslint: bin/eslint.js - checksum: 1fd31533086c1b72f86770a4d9d7058ee8b4643fd1cfd10c7aac1ecb8725698e88352a87805cf4b2ce890aa35947df4b4da9655fb7fdfa60dbb448a43f6ebcf1 + checksum: 81abddb21e540dcd509ba08fdf524b494cbda69a62ffce2a61b5adfcdeb3cbf713f72c6cbb42932333decb4b067ae7a89e4cb5e908e0d42e4287d4f357576a72 languageName: node linkType: hard @@ -13479,11 +12878,11 @@ __metadata: linkType: hard "esquery@npm:^1.4.2": - version: 1.6.0 - resolution: "esquery@npm:1.6.0" + version: 1.5.0 + resolution: "esquery@npm:1.5.0" dependencies: estraverse: "npm:^5.1.0" - checksum: cb9065ec605f9da7a76ca6dadb0619dfb611e37a81e318732977d90fab50a256b95fee2d925fba7c2f3f0523aa16f91587246693bc09bc34d5a59575fe6e93d2 + checksum: a084bd049d954cc88ac69df30534043fb2aee5555b56246493f42f27d1e168f00d9e5d4192e46f10290d312dc30dc7d58994d61a609c579c1219d636996f9213 languageName: node linkType: hard @@ -13727,6 +13126,23 @@ __metadata: languageName: node linkType: hard +"execa@npm:^7.1.1": + version: 7.2.0 + resolution: "execa@npm:7.2.0" + dependencies: + cross-spawn: "npm:^7.0.3" + get-stream: "npm:^6.0.1" + human-signals: "npm:^4.3.0" + is-stream: "npm:^3.0.0" + merge-stream: "npm:^2.0.0" + npm-run-path: "npm:^5.1.0" + onetime: "npm:^6.0.0" + signal-exit: "npm:^3.0.7" + strip-final-newline: "npm:^3.0.0" + checksum: 098cd6a1bc26d509e5402c43f4971736450b84d058391820c6f237aeec6436963e006fd8423c9722f148c53da86aa50045929c7278b5522197dff802d10f9885 + languageName: node + linkType: hard + "execa@npm:^8.0.1": version: 8.0.1 resolution: "execa@npm:8.0.1" @@ -14070,7 +13486,7 @@ __metadata: languageName: node linkType: hard -"fast-glob@npm:^3.2.5, fast-glob@npm:^3.2.9": +"fast-glob@npm:^3.2.5, fast-glob@npm:^3.2.9, fast-glob@npm:^3.3.0": version: 3.3.1 resolution: "fast-glob@npm:3.3.1" dependencies: @@ -14737,7 +14153,7 @@ __metadata: languageName: node linkType: hard -"get-stream@npm:^6.0.0": +"get-stream@npm:^6.0.0, get-stream@npm:^6.0.1": version: 6.0.1 resolution: "get-stream@npm:6.0.1" checksum: 49825d57d3fd6964228e6200a58169464b8e8970489b3acdc24906c782fb7f01f9f56f8e6653c4a50713771d6658f7cfe051e5eb8c12e334138c9c918b296341 @@ -14877,11 +14293,11 @@ __metadata: linkType: hard "globals@npm:^13.19.0": - version: 13.24.0 - resolution: "globals@npm:13.24.0" + version: 13.20.0 + resolution: "globals@npm:13.20.0" dependencies: type-fest: "npm:^0.20.2" - checksum: d3c11aeea898eb83d5ec7a99508600fbe8f83d2cf00cbb77f873dbf2bcb39428eff1b538e4915c993d8a3b3473fa71eeebfe22c9bb3a3003d1e26b1f2c8a42cd + checksum: 9a028f136f1e7a3574689f430f7d57faa0d699c4c7e92ade00b02882a892be31c314d50dff07b48e607283013117bb8a997406d03a1f7ab4a33a005eb16efd6c languageName: node linkType: hard @@ -15334,6 +14750,13 @@ __metadata: languageName: node linkType: hard +"human-signals@npm:^4.3.0": + version: 4.3.1 + resolution: "human-signals@npm:4.3.1" + checksum: 40498b33fe139f5cc4ef5d2f95eb1803d6318ac1b1c63eaf14eeed5484d26332c828de4a5a05676b6c83d7b9e57727c59addb4b1dea19cb8d71e83689e5b336c + languageName: node + linkType: hard + "human-signals@npm:^5.0.0": version: 5.0.0 resolution: "human-signals@npm:5.0.0" @@ -18689,13 +18112,6 @@ __metadata: languageName: node linkType: hard -"node-releases@npm:^2.0.18": - version: 2.0.18 - resolution: "node-releases@npm:2.0.18" - checksum: 786ac9db9d7226339e1dc84bbb42007cb054a346bd9257e6aa154d294f01bc6a6cddb1348fa099f079be6580acbb470e3c048effd5f719325abd0179e566fd27 - languageName: node - linkType: hard - "node-stream-zip@npm:^1.9.1": version: 1.15.0 resolution: "node-stream-zip@npm:1.15.0" @@ -19000,6 +18416,18 @@ __metadata: languageName: node linkType: hard +"open@npm:^9.1.0": + version: 9.1.0 + resolution: "open@npm:9.1.0" + dependencies: + default-browser: "npm:^4.0.0" + define-lazy-prop: "npm:^3.0.0" + is-inside-container: "npm:^1.0.0" + is-wsl: "npm:^2.2.0" + checksum: 8073ec0dd8994a7a7d9bac208bd17d093993a65ce10f2eb9b62b6d3a91c9366ae903938a237c275493c130171d339f6dcbdd2a2de7e32953452c0867b97825af + languageName: node + linkType: hard + "optionator@npm:^0.9.3": version: 0.9.3 resolution: "optionator@npm:0.9.3" @@ -21062,6 +20490,15 @@ __metadata: languageName: node linkType: hard +"run-applescript@npm:^5.0.0": + version: 5.0.0 + resolution: "run-applescript@npm:5.0.0" + dependencies: + execa: "npm:^5.0.0" + checksum: f9977db5770929f3f0db434b8e6aa266498c70dec913c84320c0a06add510cf44e3a048c44da088abee312006f9cbf572fd065cdc8f15d7682afda8755f4114c + languageName: node + linkType: hard + "run-parallel@npm:^1.1.9": version: 1.2.0 resolution: "run-parallel@npm:1.2.0" @@ -22076,12 +21513,12 @@ __metadata: linkType: hard "synckit@npm:^0.8.5": - version: 0.8.8 - resolution: "synckit@npm:0.8.8" + version: 0.8.5 + resolution: "synckit@npm:0.8.5" dependencies: - "@pkgr/core": "npm:^0.1.0" - tslib: "npm:^2.6.2" - checksum: c3d3aa8e284f3f84f2f868b960c9f49239b364e35f6d20825a448449a3e9c8f49fe36cdd5196b30615682f007830d46f2ea354003954c7336723cb821e4b6519 + "@pkgr/utils": "npm:^2.3.1" + tslib: "npm:^2.5.0" + checksum: 9827f828cabc404b3a147c38f824c8d5b846eb6f65189d965aa0b71ea8ecda5048f8f50b4bdfd8813148844175233cff56c6bc8d87a7118cf10707df870519f4 languageName: node linkType: hard @@ -22333,6 +21770,13 @@ __metadata: languageName: node linkType: hard +"titleize@npm:^3.0.0": + version: 3.0.0 + resolution: "titleize@npm:3.0.0" + checksum: 5ae6084ba299b5782f95e3fe85ea9f0fa4d74b8ae722b6b3208157e975589fbb27733aeba4e5080fa9314a856044ef52caa61b87caea4b1baade951a55c06336 + languageName: node + linkType: hard + "tmp@npm:^0.0.33": version: 0.0.33 resolution: "tmp@npm:0.0.33" @@ -22517,7 +21961,7 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^2.0.0, tslib@npm:^2.0.1, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.4.0": +"tslib@npm:^2.0.0, tslib@npm:^2.0.1, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.4.0, tslib@npm:^2.5.0, tslib@npm:^2.6.0": version: 2.6.1 resolution: "tslib@npm:2.6.1" checksum: a0382d386f5f1d6e3a39ab22bc56d1e08493da99ab3daf550e63bae6c08fdd6dd4fd20623ef387cad8262ce3fede98439257054fc025f2103cd4603b4509a052 @@ -22531,13 +21975,6 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^2.6.2": - version: 2.7.0 - resolution: "tslib@npm:2.7.0" - checksum: 469e1d5bf1af585742128827000711efa61010b699cb040ab1800bcd3ccdd37f63ec30642c9e07c4439c1db6e46345582614275daca3e0f4abae29b0083f04a6 - languageName: node - linkType: hard - "tsutils@npm:^3.21.0": version: 3.21.0 resolution: "tsutils@npm:3.21.0" @@ -23061,6 +22498,13 @@ __metadata: languageName: node linkType: hard +"untildify@npm:^4.0.0": + version: 4.0.0 + resolution: "untildify@npm:4.0.0" + checksum: d758e624c707d49f76f7511d75d09a8eda7f2020d231ec52b67ff4896bcf7013be3f9522d8375f57e586e9a2e827f5641c7e06ee46ab9c435fc2b2b2e9de517a + languageName: node + linkType: hard + "untun@npm:^0.1.3": version: 0.1.3 resolution: "untun@npm:0.1.3"