@dodoex/wallet-web3-react 0.0.1-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/babel.config.js +9 -0
  2. package/dist/index.js +72 -0
  3. package/dist/types/ClientProvider.d.ts +2 -0
  4. package/dist/types/LangProvider.d.ts +11 -0
  5. package/dist/types/WalletConnect/AccountPage.d.ts +11 -0
  6. package/dist/types/WalletConnect/ActivityList.d.ts +20 -0
  7. package/dist/types/WalletConnect/ConnectAlchemy/index.d.ts +6 -0
  8. package/dist/types/WalletConnect/ConnectAlchemy/useConnectAlchemy.d.ts +12 -0
  9. package/dist/types/WalletConnect/ConnectLedger/ErrorDialog.d.ts +4 -0
  10. package/dist/types/WalletConnect/ConnectLedger/LoadingDialog.d.ts +3 -0
  11. package/dist/types/WalletConnect/ConnectLedger/LockedDialog.d.ts +4 -0
  12. package/dist/types/WalletConnect/ConnectLedger/ProtocolDialog.d.ts +6 -0
  13. package/dist/types/WalletConnect/ConnectLedger/SelectAddressDialog.d.ts +8 -0
  14. package/dist/types/WalletConnect/ConnectLedger/SelectPathDialog.d.ts +5 -0
  15. package/dist/types/WalletConnect/ConnectLedger/helper.d.ts +2 -0
  16. package/dist/types/WalletConnect/ConnectLedger/index.d.ts +6 -0
  17. package/dist/types/WalletConnect/ConnectPage.d.ts +9 -0
  18. package/dist/types/WalletConnect/HasBalanceTokenList.d.ts +4 -0
  19. package/dist/types/WalletConnect/ReceiveTokenPage.d.ts +4 -0
  20. package/dist/types/WalletConnect/SendTokenPage.d.ts +5 -0
  21. package/dist/types/WalletConnect/WalletDialog.d.ts +6 -0
  22. package/dist/types/WalletConnectProvider.d.ts +72 -0
  23. package/dist/types/components/AddressWithLinkAndCopy.d.ts +28 -0
  24. package/dist/types/components/Dialog.d.ts +15 -0
  25. package/dist/types/components/TokenLogo.d.ts +26 -0
  26. package/dist/types/components/WalletTag.d.ts +7 -0
  27. package/dist/types/constants/localstorage.d.ts +6 -0
  28. package/dist/types/hooks/useConnectWalet.d.ts +30 -0
  29. package/dist/types/hooks/useFetchFiatPrice.d.ts +3 -0
  30. package/dist/types/hooks/useFetchTokensBalance.d.ts +14 -0
  31. package/dist/types/hooks/useHasBalanceTokenList.d.ts +21 -0
  32. package/dist/types/hooks/useTransactionList.d.ts +272 -0
  33. package/dist/types/index.d.ts +4 -0
  34. package/dist/types/utils/formatter.d.ts +20 -0
  35. package/dist/types/utils/time.d.ts +3 -0
  36. package/dist/types/utils/utils.d.ts +2 -0
  37. package/lingui.config.ts +13 -0
  38. package/locales/en.po +251 -0
  39. package/locales/en.ts +1 -0
  40. package/locales/zh.po +249 -0
  41. package/locales/zh.ts +1 -0
  42. package/package.json +68 -0
  43. package/rollup.config.mjs +64 -0
  44. package/src/ClientProvider.tsx +15 -0
  45. package/src/LangProvider.tsx +32 -0
  46. package/src/WalletConnect/AccountPage.tsx +535 -0
  47. package/src/WalletConnect/ActivityList.tsx +597 -0
  48. package/src/WalletConnect/ConnectAlchemy/index.tsx +246 -0
  49. package/src/WalletConnect/ConnectAlchemy/useConnectAlchemy.ts +101 -0
  50. package/src/WalletConnect/ConnectLedger/ErrorDialog.tsx +61 -0
  51. package/src/WalletConnect/ConnectLedger/LoadingDialog.tsx +106 -0
  52. package/src/WalletConnect/ConnectLedger/LockedDialog.tsx +54 -0
  53. package/src/WalletConnect/ConnectLedger/ProtocolDialog.tsx +61 -0
  54. package/src/WalletConnect/ConnectLedger/SelectAddressDialog.tsx +326 -0
  55. package/src/WalletConnect/ConnectLedger/SelectPathDialog.tsx +68 -0
  56. package/src/WalletConnect/ConnectLedger/helper.ts +14 -0
  57. package/src/WalletConnect/ConnectLedger/index.tsx +89 -0
  58. package/src/WalletConnect/ConnectPage.tsx +459 -0
  59. package/src/WalletConnect/HasBalanceTokenList.tsx +201 -0
  60. package/src/WalletConnect/ReceiveTokenPage.tsx +143 -0
  61. package/src/WalletConnect/SendTokenPage.tsx +249 -0
  62. package/src/WalletConnect/WalletDialog.tsx +72 -0
  63. package/src/WalletConnectProvider.tsx +54 -0
  64. package/src/components/AddressWithLinkAndCopy.tsx +200 -0
  65. package/src/components/Dialog.tsx +155 -0
  66. package/src/components/TokenLogo.tsx +165 -0
  67. package/src/components/WalletTag.tsx +113 -0
  68. package/src/constants/localstorage.ts +22 -0
  69. package/src/hooks/useConnectWalet.ts +144 -0
  70. package/src/hooks/useFetchFiatPrice.ts +51 -0
  71. package/src/hooks/useFetchTokensBalance.ts +52 -0
  72. package/src/hooks/useHasBalanceTokenList.ts +157 -0
  73. package/src/hooks/useTransactionList.ts +87 -0
  74. package/src/index.tsx +4 -0
  75. package/src/utils/formatter.ts +102 -0
  76. package/src/utils/time.ts +21 -0
  77. package/src/utils/utils.ts +8 -0
  78. package/tsconfig.json +22 -0
@@ -0,0 +1,2 @@
1
+ /// <reference types="react" />
2
+ export default function ClientProvider({ children }: React.PropsWithChildren): JSX.Element;
@@ -0,0 +1,11 @@
1
+ import { ReactNode } from 'react';
2
+ export declare const supportedLang: string[];
3
+ export type SupportedLang = typeof supportedLang[number];
4
+ export declare const defaultLang: SupportedLang;
5
+ interface LangProviderProps {
6
+ locale?: SupportedLang;
7
+ children: ReactNode;
8
+ }
9
+ export declare function loadI18(locale?: SupportedLang): Promise<void>;
10
+ export declare function LangProvider({ locale, children }: LangProviderProps): JSX.Element;
11
+ export default LangProvider;
@@ -0,0 +1,11 @@
1
+ import WalletWeb3 from '@dodoex/wallet-web3';
2
+ import React from 'react';
3
+ declare function AccountPage({ chainId, account, walletWeb3, onClose, setShowSendTokenPage, setShowReceiveTokenPage, }: {
4
+ chainId: number;
5
+ account?: string;
6
+ walletWeb3?: WalletWeb3;
7
+ onClose: () => void;
8
+ setShowSendTokenPage: React.Dispatch<React.SetStateAction<boolean>>;
9
+ setShowReceiveTokenPage: React.Dispatch<React.SetStateAction<boolean>>;
10
+ }): JSX.Element | null;
11
+ export default AccountPage;
@@ -0,0 +1,20 @@
1
+ import { NoticeTransactionList, useTransactionList } from '../hooks/useTransactionList';
2
+ export default function ActivityList({ getScrollParent, fetchTransactionQuery, }: {
3
+ getScrollParent: () => HTMLDivElement | null;
4
+ fetchTransactionQuery: ReturnType<typeof useTransactionList>;
5
+ }): JSX.Element;
6
+ export declare function TransactionList({ account, isLoading, isFetchingNextPage, list, }: {
7
+ account: string | undefined;
8
+ isLoading?: boolean;
9
+ isFetchingNextPage?: boolean;
10
+ list: NoticeTransactionList;
11
+ }): JSX.Element;
12
+ export declare function SubmissionStatusIcon({ status }: {
13
+ status: string;
14
+ }): JSX.Element | null;
15
+ export declare function getTitleText(brief: string, status: string): string | null;
16
+ export declare function getSubmitTitleByBrief(brief: string): string | null;
17
+ export declare function SubmissionTypeIcon({ brief, chainId, }: {
18
+ brief: string;
19
+ chainId: number;
20
+ }): JSX.Element | null;
@@ -0,0 +1,6 @@
1
+ import WalletWeb3 from '@dodoex/wallet-web3';
2
+ export default function ConnectAlchemy({ open, walletWeb3, setOpen, }: {
3
+ open: boolean;
4
+ walletWeb3: WalletWeb3;
5
+ setOpen: (open: boolean) => void;
6
+ }): JSX.Element;
@@ -0,0 +1,12 @@
1
+ import WalletWeb3, { ConnectorParams } from '@dodoex/wallet-web3';
2
+ export type AlchemyParams = Exclude<Partial<ConnectorParams['alchemyParams']>, undefined>;
3
+ export declare const USERNAME_PREFIX = "DODO";
4
+ export declare const alchemyWallet: import("@dodoex/wallet-web3").Wallet;
5
+ export declare function checkAlchemyCache(chainId: number): Promise<boolean>;
6
+ export declare function useConnectAlchemyQuery({ walletWeb3, showAlchemyConnect, }: {
7
+ walletWeb3: WalletWeb3;
8
+ showAlchemyConnect: () => void;
9
+ }): void;
10
+ export declare function useConnectAlchemy({ walletWeb3 }: {
11
+ walletWeb3: WalletWeb3;
12
+ }): import("@tanstack/react-query/build/legacy/types").UseMutationResult<void, Error, import("@alchemy/aa-alchemy").AuthParams | undefined, unknown>;
@@ -0,0 +1,4 @@
1
+ export default function ErrorDialog({ error, onClose, }: {
2
+ error?: string;
3
+ onClose: () => void;
4
+ }): JSX.Element;
@@ -0,0 +1,3 @@
1
+ export default function LoadingDialog({ on }: {
2
+ on: boolean;
3
+ }): JSX.Element;
@@ -0,0 +1,4 @@
1
+ export default function LockedDialog({ on, onClose, }: {
2
+ on: boolean;
3
+ onClose: () => void;
4
+ }): JSX.Element;
@@ -0,0 +1,6 @@
1
+ export default function ProtocolDialog({ on, chainId, onClose, onNext, }: {
2
+ on: boolean;
3
+ chainId: number;
4
+ onClose: () => void;
5
+ onNext: () => void;
6
+ }): JSX.Element;
@@ -0,0 +1,8 @@
1
+ export default function SelectAddressDialog({ on, path, chainId, onClose, onNext, onError, }: {
2
+ on: boolean;
3
+ path: string;
4
+ chainId: number;
5
+ onClose: () => void;
6
+ onNext: (path: string, address: string) => void;
7
+ onError: (error: any) => void;
8
+ }): JSX.Element;
@@ -0,0 +1,5 @@
1
+ export default function SelectPathDialog({ on, onClose, onNext, }: {
2
+ on: boolean;
3
+ onClose: () => void;
4
+ onNext: (path: string) => void;
5
+ }): JSX.Element;
@@ -0,0 +1,2 @@
1
+ export declare function getTransport(): Promise<import("@ledgerhq/hw-transport-webhid").default>;
2
+ export declare function getAccountList(pathRule: string, page?: number | undefined, pageSize?: number): Promise<import("@dodoex/wallet-web3/dist/providers/connector/ledgerUSB").HDAccountItem[]>;
@@ -0,0 +1,6 @@
1
+ export default function ConnectLedger({ on, chainId, onClose, onConnect, }: {
2
+ on: boolean;
3
+ chainId: number | undefined;
4
+ onClose: () => void;
5
+ onConnect: (path: string, account: string) => void;
6
+ }): JSX.Element;
@@ -0,0 +1,9 @@
1
+ import WalletWeb3 from '@dodoex/wallet-web3';
2
+ declare function ConnectPage({ chainId, account, walletWeb3, showOtherInjectWallet, onClose, }: {
3
+ chainId: number;
4
+ account?: string;
5
+ walletWeb3?: WalletWeb3;
6
+ showOtherInjectWallet?: boolean;
7
+ onClose: () => void;
8
+ }): JSX.Element;
9
+ export default ConnectPage;
@@ -0,0 +1,4 @@
1
+ import { useHasBalanceTokenList } from '../hooks/useHasBalanceTokenList';
2
+ export default function HasBalanceTokenList({ balanceListData, }: {
3
+ balanceListData: ReturnType<typeof useHasBalanceTokenList>;
4
+ }): JSX.Element;
@@ -0,0 +1,4 @@
1
+ export default function ReceiveTokenPage({ onClose, onBack, }: {
2
+ onClose: () => void;
3
+ onBack: () => void;
4
+ }): JSX.Element | null;
@@ -0,0 +1,5 @@
1
+ export default function SendTokenPage({ open, onClose, onBack, }: {
2
+ open?: boolean;
3
+ onClose: () => void;
4
+ onBack: () => void;
5
+ }): JSX.Element;
@@ -0,0 +1,6 @@
1
+ import WalletWeb3 from '@dodoex/wallet-web3';
2
+ export default function WalletDialog({ open, onClose, walletWeb3, }: {
3
+ open: boolean;
4
+ onClose: () => void;
5
+ walletWeb3: WalletWeb3;
6
+ }): JSX.Element;
@@ -0,0 +1,72 @@
1
+ import { GraphQLRequests, RestApiRequests } from '@dodoex/api';
2
+ import React from 'react';
3
+ import { TokenInfo } from './components/TokenLogo';
4
+ import { WalletItem } from './hooks/useConnectWalet';
5
+ export declare const WalletConnectProvider: React.Provider<{
6
+ chainId?: number | undefined;
7
+ termsOfServiceLink?: React.ReactNode;
8
+ connectTimeout?: number | undefined;
9
+ graphQLRequests?: GraphQLRequests | undefined;
10
+ tokenList: Array<TokenInfo>;
11
+ restApiRequests?: RestApiRequests | undefined;
12
+ SendTokenPage?: ((params: {
13
+ open?: boolean;
14
+ onClose: () => void;
15
+ onBack: () => void;
16
+ }) => JSX.Element) | undefined;
17
+ encryptFiatPriceToken?: (() => string) | undefined;
18
+ loadAccountListEthBalance?: ((accountList: string[], chainId?: number) => Promise<Map<string, number | null>>) | undefined;
19
+ getChain: (chainId: number) => null | {
20
+ name: string;
21
+ scanUrl: string;
22
+ gasToken: {
23
+ symbol: string;
24
+ decimals: number;
25
+ };
26
+ logo: React.ReactNode;
27
+ };
28
+ getTokenLogoUrl?: ((op: {
29
+ chainId?: number | null;
30
+ address?: string | null;
31
+ width?: number;
32
+ height?: number;
33
+ }) => string | null) | undefined;
34
+ onConnectTimeout?: (() => void) | undefined;
35
+ switchNetwork?: ((chainId: number) => void) | undefined;
36
+ onConnected?: ((chainId: number, wallet: WalletItem) => void) | undefined;
37
+ onClickToken?: ((token: TokenInfo) => void) | undefined;
38
+ }>;
39
+ export declare const useWalletConnectContext: () => {
40
+ chainId?: number | undefined;
41
+ termsOfServiceLink?: React.ReactNode;
42
+ connectTimeout?: number | undefined;
43
+ graphQLRequests?: GraphQLRequests | undefined;
44
+ tokenList: Array<TokenInfo>;
45
+ restApiRequests?: RestApiRequests | undefined;
46
+ SendTokenPage?: ((params: {
47
+ open?: boolean;
48
+ onClose: () => void;
49
+ onBack: () => void;
50
+ }) => JSX.Element) | undefined;
51
+ encryptFiatPriceToken?: (() => string) | undefined;
52
+ loadAccountListEthBalance?: ((accountList: string[], chainId?: number) => Promise<Map<string, number | null>>) | undefined;
53
+ getChain: (chainId: number) => null | {
54
+ name: string;
55
+ scanUrl: string;
56
+ gasToken: {
57
+ symbol: string;
58
+ decimals: number;
59
+ };
60
+ logo: React.ReactNode;
61
+ };
62
+ getTokenLogoUrl?: ((op: {
63
+ chainId?: number | null;
64
+ address?: string | null;
65
+ width?: number;
66
+ height?: number;
67
+ }) => string | null) | undefined;
68
+ onConnectTimeout?: (() => void) | undefined;
69
+ switchNetwork?: ((chainId: number) => void) | undefined;
70
+ onConnected?: ((chainId: number, wallet: WalletItem) => void) | undefined;
71
+ onClickToken?: ((token: TokenInfo) => void) | undefined;
72
+ };
@@ -0,0 +1,28 @@
1
+ /// <reference types="react" />
2
+ import { BoxProps } from '@dodoex/components';
3
+ export declare function truncatePoolAddress(address: string): string;
4
+ interface AddressTextProps {
5
+ sx?: BoxProps['sx'];
6
+ truncate?: boolean;
7
+ address: string;
8
+ disabledAddress?: boolean;
9
+ addressHoverColor?: string;
10
+ addressHoverShowIcon?: boolean;
11
+ handleOpen?: (event: React.MouseEvent<HTMLDivElement, MouseEvent>, type: 'address' | 'icon') => void;
12
+ }
13
+ interface Props extends AddressTextProps {
14
+ showCopy?: boolean;
15
+ size?: 'small' | 'medium' | 'big';
16
+ newTab?: boolean;
17
+ iconSize?: number;
18
+ iconSpace?: number;
19
+ iconDarkHover?: boolean;
20
+ customChainId?: number;
21
+ onAddressClick?: (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => void;
22
+ }
23
+ export declare function AddressText({ truncate, address, disabledAddress, sx, handleOpen, addressHoverColor, addressHoverShowIcon, }: AddressTextProps & {
24
+ typography?: string;
25
+ domain?: string;
26
+ }): JSX.Element;
27
+ export declare function AddressWithLink({ address, truncate, size, iconSize, iconSpace: iconSpaceProps, sx, iconDarkHover, disabledAddress, addressHoverColor, addressHoverShowIcon, customChainId, handleOpen, onAddressClick, }: Props): JSX.Element;
28
+ export {};
@@ -0,0 +1,15 @@
1
+ import { ModalProps } from '@mui/base/Modal';
2
+ import React from 'react';
3
+ import { BoxProps } from '@dodoex/components';
4
+ export declare function DialogTitle({ children, center, onClose, sx, }: React.PropsWithChildren<{
5
+ center?: boolean;
6
+ onClose?: () => void;
7
+ sx?: BoxProps['sx'];
8
+ }>): JSX.Element;
9
+ export declare function DialogBase({ className, slots, ...props }: ModalProps): JSX.Element;
10
+ export default function Dialog({ children, sx, bodySx, slots, width, ...props }: Omit<ModalProps, 'children'> & {
11
+ children: React.ReactNode;
12
+ sx?: BoxProps['sx'];
13
+ bodySx?: BoxProps['sx'];
14
+ width?: number;
15
+ }): JSX.Element;
@@ -0,0 +1,26 @@
1
+ import React from 'react';
2
+ import { BoxProps } from '@dodoex/components';
3
+ export interface TokenInfo {
4
+ readonly chainId: number;
5
+ readonly address: string;
6
+ readonly symbol?: string;
7
+ readonly name?: string;
8
+ readonly decimals?: number;
9
+ readonly logoURI?: string;
10
+ }
11
+ export interface TokenLogoProps {
12
+ address?: string;
13
+ logoURI?: string;
14
+ width?: number;
15
+ height?: number;
16
+ marginRight?: number;
17
+ url?: string;
18
+ zIndex?: number;
19
+ sx?: BoxProps['sx'];
20
+ chainId?: number;
21
+ noShowChain?: boolean;
22
+ noBorder?: boolean;
23
+ chainSize?: number;
24
+ logoOffset?: number;
25
+ }
26
+ export default function TokenLogo({ width, height, marginRight, zIndex, address, logoURI, sx, chainId, noBorder, chainSize, logoOffset: logoOffsetProps, }: TokenLogoProps): React.ReactElement;
@@ -0,0 +1,7 @@
1
+ import { WalletItem } from '../hooks/useConnectWalet';
2
+ export default function WalletTag({ loading, wallet, isChecked, installed, }: {
3
+ loading: boolean;
4
+ wallet: WalletItem;
5
+ isChecked: boolean;
6
+ installed: boolean;
7
+ }): JSX.Element | null;
@@ -0,0 +1,6 @@
1
+ interface FiatPriceCacheList {
2
+ [k: string]: number;
3
+ }
4
+ export declare function setTokenFiatPriceList(value: FiatPriceCacheList): void;
5
+ export declare function getTokenFiatPriceList(): FiatPriceCacheList;
6
+ export {};
@@ -0,0 +1,30 @@
1
+ import WalletWeb3, { Wallet } from '@dodoex/wallet-web3';
2
+ export interface WalletItem {
3
+ title: string;
4
+ icon: string;
5
+ currentType: string;
6
+ onClick: (providerPackageOptions?: Partial<WalletWeb3['providerConfig']>) => void;
7
+ disabled?: boolean;
8
+ isLastConnection: boolean;
9
+ isInstalled?: boolean;
10
+ }
11
+ export declare const useConnectWallet: ({ walletWeb3, matchTestChain, showOtherInjectWallet, selectedChainId, hasTermsOfServiceLink, }: {
12
+ walletWeb3?: WalletWeb3 | undefined;
13
+ /** Match the test chain and display the first bit of showName and chainIds as the matching test chain. If it is false or not transmitted, the main network information will still be displayed. */
14
+ matchTestChain?: boolean | undefined;
15
+ /**
16
+ * No need to display when switching wallets
17
+ */
18
+ showOtherInjectWallet?: boolean | undefined;
19
+ selectedChainId?: number | undefined;
20
+ hasTermsOfServiceLink?: boolean | undefined;
21
+ }) => {
22
+ walletList: WalletItem[];
23
+ userReadAndChecked: boolean;
24
+ handleChangeUserReadAndChecked: (val: boolean) => void;
25
+ activeWalletType: import("@dodoex/wallet-web3").WalletType | undefined;
26
+ };
27
+ export declare const connectToWallet: (walletWeb3: WalletWeb3 | undefined, wallet: Wallet, config?: Partial<WalletWeb3['providerConfig']>) => Promise<import("@ethersproject/providers").JsonRpcProvider | undefined>;
28
+ export declare function useWalletListByNetwork(chainId?: number, walletWeb3?: WalletWeb3, showOtherInjectWallet?: boolean, closeSingleWalletConnectModal?: () => void): {
29
+ walletList: WalletItem[];
30
+ };
@@ -0,0 +1,3 @@
1
+ import { TokenInfo } from '../components/TokenLogo';
2
+ export declare function getFiatPriceQueryKeys(tokens?: TokenInfo[]): (string | TokenInfo[] | undefined)[];
3
+ export declare function useFetchFiatPrice(tokens: TokenInfo[]): import("@tanstack/react-query/build/legacy/types").UseQueryResult<Map<string, number>, Error>;
@@ -0,0 +1,14 @@
1
+ import { TokenInfo } from '../components/TokenLogo';
2
+ type TokenInfoMap = Map<string, bigint>;
3
+ export default function useFetchTokensBalance({ account, tokenList, blockNumber, skip, }: {
4
+ account: string | undefined;
5
+ tokenList?: TokenInfo[];
6
+ blockNumber?: number;
7
+ skip?: boolean;
8
+ }): {
9
+ tokenInfoMap: TokenInfoMap;
10
+ data: (bigint | undefined)[];
11
+ isPending: boolean;
12
+ isLoading: boolean;
13
+ };
14
+ export {};
@@ -0,0 +1,21 @@
1
+ import BigNumber from 'bignumber.js';
2
+ export declare function useHasBalanceTokenList({ account, chainId, visible, }: {
3
+ account: string | undefined;
4
+ chainId: number;
5
+ visible: boolean;
6
+ }): {
7
+ tokenLoading: boolean;
8
+ hasBalanceList: {
9
+ fiatPriceBalance: BigNumber | undefined;
10
+ balance: BigNumber;
11
+ chainId: number;
12
+ address: string;
13
+ symbol?: string | undefined;
14
+ name?: string | undefined;
15
+ decimals?: number | undefined;
16
+ logoURI?: string | undefined;
17
+ }[];
18
+ fiatPriceQuery: import("@tanstack/react-query/build/legacy/types").UseQueryResult<Map<string, number>, Error>;
19
+ allFiatPriceBalance: BigNumber;
20
+ allFiatPriceBalanceLoading: boolean;
21
+ };