From 6e185f54b18aa0a9eea875f034d1e90e961a869f Mon Sep 17 00:00:00 2001
From: nuanyang233 <nuanyang233@gmail.com>
Date: Thu, 26 May 2022 17:34:15 +0800
Subject: [PATCH 1/5] fix: add missing deps (#6356)

---
 .../src/components/InjectedComponents/ProfileTabContent.tsx     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/mask/src/components/InjectedComponents/ProfileTabContent.tsx b/packages/mask/src/components/InjectedComponents/ProfileTabContent.tsx
index 0487f9d1b095..aaf461822524 100644
--- a/packages/mask/src/components/InjectedComponents/ProfileTabContent.tsx
+++ b/packages/mask/src/components/InjectedComponents/ProfileTabContent.tsx
@@ -124,7 +124,7 @@ export function ProfileTabContent(props: ProfileTabContentProps) {
                 ? displayPlugins?.find((tab) => tab?.pluginID === PluginId.NextID)?.ID
                 : selectedTabId
         return getTabContent(tabId ?? '')
-    }, [selectedTabId, identity.identifier?.userId])
+    }, [selectedTabId, identity.identifier?.userId, currentAccountNotConnectPersona])
 
     if (hidden) return null
 

From 836f5f5535702cd8ad948a320f7eee88d142f3ba Mon Sep 17 00:00:00 2001
From: Hancheng Zhou <z308114274@gmail.com>
Date: Thu, 26 May 2022 17:44:11 +0800
Subject: [PATCH 2/5] fix(ui): claim button && mind fb tool box hint (#6337)

---
 cspell.json                                   |  1 +
 .../InjectedComponents/ToolboxUnstyled.tsx    |  5 ++++-
 .../DashboardComponents/ActionButton.tsx      | 21 +++++++++++++++----
 .../plugins/ITO/SNSAdaptor/ClaimAllDialog.tsx |  2 ++
 .../facebook.com/injection/Toolbar.tsx        |  3 ++-
 .../facebook.com/injection/ToolbarUI.tsx      | 19 +++++++++--------
 .../minds.com/customization/custom.ts         |  3 +++
 .../minds.com/injection/ToolboxHint_UI.tsx    |  4 ++--
 .../minds.com/utils/selector.ts               |  4 +---
 .../src/web3/UI/EthereumChainBoundary.tsx     |  3 +++
 10 files changed, 45 insertions(+), 20 deletions(-)

diff --git a/cspell.json b/cspell.json
index 28e4e04fbc54..4dbe240d7ac5 100644
--- a/cspell.json
+++ b/cspell.json
@@ -286,6 +286,7 @@
         "redpackets",
         "renfil",
         "shapeclip",
+        "ssrb",
         "steth",
         "swaptoken",
         "testweb",
diff --git a/packages/mask/src/components/InjectedComponents/ToolboxUnstyled.tsx b/packages/mask/src/components/InjectedComponents/ToolboxUnstyled.tsx
index 106098381aeb..6a2593c93cca 100644
--- a/packages/mask/src/components/InjectedComponents/ToolboxUnstyled.tsx
+++ b/packages/mask/src/components/InjectedComponents/ToolboxUnstyled.tsx
@@ -91,6 +91,7 @@ export function ToolboxHintUnstyled(props: ToolboxHintProps) {
 function ToolboxHintForApplication(props: ToolboxHintProps) {
     const {
         ListItemButton = MuiListItemButton,
+        ListItemIcon = MuiListItemIcon,
         Container = 'div',
         Typography = MuiTypography,
         iconSize = 24,
@@ -104,7 +105,9 @@ function ToolboxHintForApplication(props: ToolboxHintProps) {
         <GuideStep step={1} total={4} tip={t('user_guide_tip_1')}>
             <Container>
                 <ListItemButton onClick={openDialog}>
-                    <MaskIcon style={{ width: iconSize, height: iconSize }} />
+                    <ListItemIcon>
+                        <MaskIcon style={{ width: iconSize, height: iconSize }} />
+                    </ListItemIcon>
                     {mini ? null : (
                         <ListItemText
                             primary={
diff --git a/packages/mask/src/extension/options-page/DashboardComponents/ActionButton.tsx b/packages/mask/src/extension/options-page/DashboardComponents/ActionButton.tsx
index d01f32ccdfcf..449c69a66a4d 100644
--- a/packages/mask/src/extension/options-page/DashboardComponents/ActionButton.tsx
+++ b/packages/mask/src/extension/options-page/DashboardComponents/ActionButton.tsx
@@ -114,8 +114,9 @@ export function ActionButtonPromise(props: ActionButtonPromiseProps) {
     } = props
 
     const [state, setState] = useState<ActionButtonPromiseState>('init')
-    const completeClass = classNames(classes.success, b.className)
-    const failClass = classNames(classes.failed, b.className)
+    const basicClass = classNames(classes.button, b.className)
+    const completeClass = classNames(basicClass, classes.success)
+    const failClass = classNames(basicClass, classes.failed)
 
     const run = () => {
         setState('wait')
@@ -144,7 +145,16 @@ export function ActionButtonPromise(props: ActionButtonPromiseProps) {
     }, [executor, noUpdateEffect])
 
     if (state === 'wait')
-        return <Button {...b} startIcon={circle} disabled={!waitingOnClick} children={waiting} onClick={cancel} />
+        return (
+            <Button
+                {...b}
+                startIcon={circle}
+                disabled={!waitingOnClick}
+                children={waiting}
+                onClick={cancel}
+                className={basicClass}
+            />
+        )
     if (state === 'complete')
         return (
             <Button
@@ -167,7 +177,7 @@ export function ActionButtonPromise(props: ActionButtonPromiseProps) {
                 onClick={failClick}
             />
         )
-    return <Button {...b} children={init} onClick={run} />
+    return <Button {...b} children={init} onClick={run} className={basicClass} />
 }
 const useStyles = makeStyles()({
     success: {
@@ -184,4 +194,7 @@ const useStyles = makeStyles()({
             backgroundColor: red[700],
         },
     },
+    button: {
+        borderRadius: 999,
+    },
 })
diff --git a/packages/mask/src/plugins/ITO/SNSAdaptor/ClaimAllDialog.tsx b/packages/mask/src/plugins/ITO/SNSAdaptor/ClaimAllDialog.tsx
index e78594152746..ce75f3efdb90 100644
--- a/packages/mask/src/plugins/ITO/SNSAdaptor/ClaimAllDialog.tsx
+++ b/packages/mask/src/plugins/ITO/SNSAdaptor/ClaimAllDialog.tsx
@@ -210,6 +210,8 @@ const useStyles = makeStyles<StyleProps>()((theme, props) => {
             margin: theme.spacing(3, 'auto'),
         },
         claimAllButton: {
+            height: 48,
+            borderRadius: 999,
             [smallQuery]: {
                 fontSize: 14,
             },
diff --git a/packages/mask/src/social-network-adaptor/facebook.com/injection/Toolbar.tsx b/packages/mask/src/social-network-adaptor/facebook.com/injection/Toolbar.tsx
index 0271d175348d..7dd832f3934f 100644
--- a/packages/mask/src/social-network-adaptor/facebook.com/injection/Toolbar.tsx
+++ b/packages/mask/src/social-network-adaptor/facebook.com/injection/Toolbar.tsx
@@ -6,7 +6,8 @@ import { ToolboxAtFacebook } from './ToolbarUI'
 export function injectToolboxHintAtFacebook(signal: AbortSignal, category: 'wallet' | 'application') {
     const watcher = new MutationObserverWatcher(toolBoxInSideBarSelector())
     startWatch(watcher, signal)
+    const hasTopNavBar = Boolean(document.querySelector<HTMLElement>('#ssrb_top_nav_start ~ [role="banner"]'))
     createReactRootShadowed(watcher.firstDOMProxy.afterShadow, { signal }).render(
-        <ToolboxAtFacebook category={category} />,
+        <ToolboxAtFacebook category={category} hasTopNavBar={hasTopNavBar} />,
     )
 }
diff --git a/packages/mask/src/social-network-adaptor/facebook.com/injection/ToolbarUI.tsx b/packages/mask/src/social-network-adaptor/facebook.com/injection/ToolbarUI.tsx
index da7d3003887c..bb5605084996 100644
--- a/packages/mask/src/social-network-adaptor/facebook.com/injection/ToolbarUI.tsx
+++ b/packages/mask/src/social-network-adaptor/facebook.com/injection/ToolbarUI.tsx
@@ -3,8 +3,8 @@ import { ToolboxHintUnstyled } from '../../../components/InjectedComponents/Tool
 
 const fbBreakPoint = 700 /** px */
 
-const Container = styled('div')`
-    padding: 0 8px;
+const Container = styled('div')<{ hasTopNavBar: boolean }>`
+    padding: 0 ${(props) => (props.hasTopNavBar ? '8px' : '4px')};
 `
 const Item = styled(ListItemButton)`
     border-radius: 8px;
@@ -16,22 +16,23 @@ const Text = styled(Typography)`
     color: var(--primary-text);
     font-weight: 500;
 `
-const Icon = styled(ListItemIcon)`
-    min-width: 46px;
+const Icon = styled(ListItemIcon)<{ hasTopNavBar: boolean }>`
+    min-width: ${(props) => (props.hasTopNavBar ? '46px' : 'auto')};
+    margin-right: ${(props) => (props.hasTopNavBar ? '0px' : '12px')};
     @media screen and (max-height: ${fbBreakPoint}px) {
-        min-width: 36px;
+        min-width: ${(props) => (props.hasTopNavBar ? '36px' : 'auto')};
     }
 `
 
-export function ToolboxAtFacebook(props: { category: 'wallet' | 'application' }) {
+export function ToolboxAtFacebook(props: { category: 'wallet' | 'application'; hasTopNavBar: boolean }) {
     const isSmall = useMediaQuery(`(max-height: ${fbBreakPoint}px)`)
     return (
         <ToolboxHintUnstyled
-            iconSize={isSmall ? 24 : 32}
-            Container={Container}
+            iconSize={isSmall || !props.hasTopNavBar ? 24 : 32}
+            Container={({ children }) => <Container hasTopNavBar={props.hasTopNavBar}>{children}</Container>}
             ListItemButton={Item}
             Typography={Text}
-            ListItemIcon={Icon}
+            ListItemIcon={({ children }) => <Icon hasTopNavBar={props.hasTopNavBar}>{children}</Icon>}
             category={props.category}
         />
     )
diff --git a/packages/mask/src/social-network-adaptor/minds.com/customization/custom.ts b/packages/mask/src/social-network-adaptor/minds.com/customization/custom.ts
index 06ffa6a859f7..e188b7bcd402 100644
--- a/packages/mask/src/social-network-adaptor/minds.com/customization/custom.ts
+++ b/packages/mask/src/social-network-adaptor/minds.com/customization/custom.ts
@@ -28,7 +28,10 @@ export function startWatchThemeColor(signal: AbortSignal) {
         if (backgroundColor)
             backgroundColorRef.value = currentTheme.value === 'light' ? 'rgb(244, 244 ,245)' : 'rgb(26, 32, 37)'
     }
+    // init
+    currentTheme.value = getBackgroundColor(document.body) === 'rgb(255,255,255)' ? 'light' : 'dark'
 
+    // update
     new MutationObserverWatcher(themeListItemSelector())
         .addListener('onAdd', updateThemeColor)
         .addListener('onChange', updateThemeColor)
diff --git a/packages/mask/src/social-network-adaptor/minds.com/injection/ToolboxHint_UI.tsx b/packages/mask/src/social-network-adaptor/minds.com/injection/ToolboxHint_UI.tsx
index de15d87e68eb..77b4a151d92c 100644
--- a/packages/mask/src/social-network-adaptor/minds.com/injection/ToolboxHint_UI.tsx
+++ b/packages/mask/src/social-network-adaptor/minds.com/injection/ToolboxHint_UI.tsx
@@ -1,5 +1,5 @@
 import { ToolboxHintUnstyled } from '../../../components/InjectedComponents/ToolboxUnstyled'
-import { styled, ListItemButton, Typography, ListItemIcon, useMediaQuery } from '@mui/material'
+import { styled, ListItemButton, Typography, ListItemIcon, useMediaQuery, useTheme } from '@mui/material'
 
 const mindsBreakPoint = 1221 /** px */
 
@@ -42,7 +42,7 @@ const Icon = styled(ListItemIcon)`
 
 export function ToolboxHintAtMinds(props: { category: 'wallet' | 'application' }) {
     const mini = useMediaQuery(`(max-width: ${mindsBreakPoint}px)`)
-
+    const mode = useTheme().palette.mode
     return (
         <ToolboxHintUnstyled
             mini={mini}
diff --git a/packages/mask/src/social-network-adaptor/minds.com/utils/selector.ts b/packages/mask/src/social-network-adaptor/minds.com/utils/selector.ts
index 47fd391b1f1b..11ba52931f6e 100644
--- a/packages/mask/src/social-network-adaptor/minds.com/utils/selector.ts
+++ b/packages/mask/src/social-network-adaptor/minds.com/utils/selector.ts
@@ -10,9 +10,7 @@ const querySelector = <T extends E, SingleMode extends boolean = true>(selector:
 export const rootSelector: () => LiveSelector<E, true> = () => querySelector<E>('m-app')
 
 export const themeListItemSelector: () => LiveSelector<HTMLAnchorElement, true> = () =>
-    querySelector<HTMLAnchorElement>(
-        'm-usermenu__v3 > div.m-user-menu.m-dropdown > ul > li:nth-child(5) .ng-star-inserted',
-    )
+    querySelector<HTMLAnchorElement>('.m-sidebarMore__dropdown > li:nth-child(7)')
 
 export const composerModalSelector: () => LiveSelector<E, true> = () => querySelector<E>('m-composer__modal')
 
diff --git a/packages/mask/src/web3/UI/EthereumChainBoundary.tsx b/packages/mask/src/web3/UI/EthereumChainBoundary.tsx
index 9626dece8fd5..b8ce3786dd41 100644
--- a/packages/mask/src/web3/UI/EthereumChainBoundary.tsx
+++ b/packages/mask/src/web3/UI/EthereumChainBoundary.tsx
@@ -48,6 +48,9 @@ const useStyles = makeStyles()((theme) => ({
         borderRadius: 4,
         padding: 10,
     },
+    switchButton: {
+        borderRadius: 999,
+    },
 }))
 
 export interface EthereumChainBoundaryProps extends withClasses<'switchButton'> {

From 0bf326e06e73210e96064b983f7571b64d877d6a Mon Sep 17 00:00:00 2001
From: UncleBill <billbill290@gmail.com>
Date: Thu, 26 May 2022 19:14:58 +0800
Subject: [PATCH 3/5] refactor: update promote text for plugins. (#6217)

* refactor: redpacket share text

* fix: promote redpacket

* fix: promote gitcoin, trader, and ITO

* fix: decorate encrypted text

* fix: share after setting up pfp

* fix: follow up reviews

* fix: promote savings

* fix: collectible and snapshot

* fix: fix after rebasing

* fix: fix typo

* fix: decorate result

* fix: add missing description

* fix: replace useCallback with useAsyncFn

* fixup! fix: replace useCallback with useAsyncFn

* fix: revert
---
 .i18n-codegen.json                            |  36 ++++++
 packages/mask/shared-ui/locales/en-US.json    | 114 ++++--------------
 packages/mask/shared-ui/locales/ja-JP.json    |  22 ----
 packages/mask/shared-ui/locales/ko-KR.json    |  60 ---------
 packages/mask/shared-ui/locales/qya-AA.json   |  77 ------------
 packages/mask/shared-ui/locales/zh-CN.json    |  73 -----------
 packages/mask/shared-ui/locales/zh-TW.json    |  51 --------
 .../components/CompositionDialog/useSubmit.ts |  65 ++++++----
 .../plugins/Avatar/SNSAdaptor/NFTAvatar.tsx   |   3 +-
 .../SNSAdaptor/MakeOfferDialog.tsx            |   4 +-
 .../Gitcoin/SNSAdaptor/DonateDialog.tsx       |  37 +++---
 .../Gitcoin/SNSAdaptor/PreviewCard.tsx        |  18 +--
 .../src/plugins/Gitcoin/SNSAdaptor/index.tsx  |  20 ++-
 packages/mask/src/plugins/Gitcoin/base.ts     |   2 +
 .../src/plugins/Gitcoin/locales/en-US.json    |  15 +++
 .../mask/src/plugins/Gitcoin/locales/index.ts |   6 +
 .../src/plugins/Gitcoin/locales/ja-JP.json    |   1 +
 .../src/plugins/Gitcoin/locales/ko-KR.json    |   1 +
 .../src/plugins/Gitcoin/locales/languages.ts  |  34 ++++++
 .../src/plugins/Gitcoin/locales/qya-AA.json   |   1 +
 .../src/plugins/Gitcoin/locales/zh-CN.json    |   1 +
 .../src/plugins/Gitcoin/locales/zh-TW.json    |   1 +
 .../mask/src/plugins/ITO/SNSAdaptor/ITO.tsx   |  23 +---
 .../plugins/ITO/SNSAdaptor/NftAirdropCard.tsx |  37 ++----
 .../src/plugins/ITO/SNSAdaptor/PoolInList.tsx |  24 ++--
 .../SNSAdaptor/hooks/useDestructCallback.ts   |  62 ++--------
 .../useSpaceStationContractClaimCallback.ts   |  84 +++----------
 .../SNSAdaptor/components/PreviewCard.tsx     |   2 +-
 .../plugins/RedPacket/SNSAdaptor/NftList.tsx  |   6 +-
 .../SNSAdaptor/NftRedPacketHistoryItem.tsx    |  20 ++-
 .../SNSAdaptor/NftRedPacketHistoryList.tsx    |  12 +-
 .../SNSAdaptor/RedPacket/OperationFooter.tsx  |  20 ++-
 .../RedPacket/SNSAdaptor/RedPacket/index.tsx  |  69 ++++++-----
 .../SNSAdaptor/RedPacketConfirmDialog.tsx     |  64 ++++------
 .../SNSAdaptor/RedPacketCreateNew.tsx         |   8 +-
 .../RedPacket/SNSAdaptor/RedPacketDialog.tsx  |  10 +-
 .../SNSAdaptor/RedPacketERC20Form.tsx         |  42 ++++---
 .../SNSAdaptor/RedPacketERC721Form.tsx        |  31 +++--
 .../SNSAdaptor/RedPacketInHistoryList.tsx     |  43 +++----
 .../RedPacket/SNSAdaptor/RedPacketNft.tsx     |  88 +++++++-------
 .../RedPacket/SNSAdaptor/RedPacketPast.tsx    |   8 +-
 .../SNSAdaptor/RedpacketMessagePanel.tsx      |   8 +-
 .../SNSAdaptor/RedpacketNftConfirmDialog.tsx  |  24 ++--
 .../SNSAdaptor/SelectNftTokenDialog.tsx       |  73 ++++-------
 .../SNSAdaptor/hooks/useCreateCallback.tsx    |  36 +-----
 .../plugins/RedPacket/SNSAdaptor/index.tsx    |   8 +-
 packages/mask/src/plugins/RedPacket/base.ts   |   2 +
 .../src/plugins/RedPacket/locales/en-US.json  |  84 +++++++++++++
 .../src/plugins/RedPacket/locales/index.ts    |   6 +
 .../src/plugins/RedPacket/locales/ja-JP.json  |   1 +
 .../src/plugins/RedPacket/locales/ko-KR.json  |   1 +
 .../plugins/RedPacket/locales/languages.ts    |  34 ++++++
 .../src/plugins/RedPacket/locales/qya-AA.json |   1 +
 .../src/plugins/RedPacket/locales/zh-CN.json  |   1 +
 .../src/plugins/RedPacket/locales/zh-TW.json  |   1 +
 .../Savings/SNSAdaptor/SavingsForm.tsx        |  18 ++-
 .../Snapshot/SNSAdaptor/VotingCard.tsx        |   8 +-
 .../Trader/SNSAdaptor/trader/Trader.tsx       |  11 +-
 packages/mask/src/plugins/Trader/base.ts      |   2 +
 .../src/plugins/Trader/locales/en-US.json     |   3 +
 .../mask/src/plugins/Trader/locales/index.ts  |   6 +
 .../src/plugins/Trader/locales/ja-JP.json     |   1 +
 .../src/plugins/Trader/locales/ko-KR.json     |   1 +
 .../src/plugins/Trader/locales/languages.ts   |  34 ++++++
 .../src/plugins/Trader/locales/qya-AA.json    |   1 +
 .../src/plugins/Trader/locales/zh-CN.json     |   1 +
 .../src/plugins/Trader/locales/zh-TW.json     |   1 +
 .../src/plugins/dHEDGE/UI/InvestDialog.tsx    |   2 +-
 .../plugins/dHEDGE/hooks/useInvestCallback.ts |   9 +-
 .../injection/NFT/NFTAvatarInTwitter.tsx      |  24 +++-
 packages/mask/src/tsconfig.json               |   1 +
 .../UI/EthereumERC20TokenApprovedBoundary.tsx |   4 +-
 .../EthereumERC721TokenApprovedBoundary.tsx   |  77 ++++++------
 packages/plugins/FileService/src/index.ts     |   2 +
 .../evm/hooks/useERC20TokenApproveCallback.ts |  70 ++---------
 ...eERC721ContractSetApproveForAllCallback.ts |  45 ++-----
 .../evm/hooks/useTransactionCallback.ts       |  16 +--
 77 files changed, 785 insertions(+), 1127 deletions(-)
 create mode 100644 packages/mask/src/plugins/Gitcoin/locales/en-US.json
 create mode 100644 packages/mask/src/plugins/Gitcoin/locales/index.ts
 create mode 100644 packages/mask/src/plugins/Gitcoin/locales/ja-JP.json
 create mode 100644 packages/mask/src/plugins/Gitcoin/locales/ko-KR.json
 create mode 100644 packages/mask/src/plugins/Gitcoin/locales/languages.ts
 create mode 100644 packages/mask/src/plugins/Gitcoin/locales/qya-AA.json
 create mode 100644 packages/mask/src/plugins/Gitcoin/locales/zh-CN.json
 create mode 100644 packages/mask/src/plugins/Gitcoin/locales/zh-TW.json
 create mode 100644 packages/mask/src/plugins/RedPacket/locales/en-US.json
 create mode 100644 packages/mask/src/plugins/RedPacket/locales/index.ts
 create mode 100644 packages/mask/src/plugins/RedPacket/locales/ja-JP.json
 create mode 100644 packages/mask/src/plugins/RedPacket/locales/ko-KR.json
 create mode 100644 packages/mask/src/plugins/RedPacket/locales/languages.ts
 create mode 100644 packages/mask/src/plugins/RedPacket/locales/qya-AA.json
 create mode 100644 packages/mask/src/plugins/RedPacket/locales/zh-CN.json
 create mode 100644 packages/mask/src/plugins/RedPacket/locales/zh-TW.json
 create mode 100644 packages/mask/src/plugins/Trader/locales/en-US.json
 create mode 100644 packages/mask/src/plugins/Trader/locales/index.ts
 create mode 100644 packages/mask/src/plugins/Trader/locales/ja-JP.json
 create mode 100644 packages/mask/src/plugins/Trader/locales/ko-KR.json
 create mode 100644 packages/mask/src/plugins/Trader/locales/languages.ts
 create mode 100644 packages/mask/src/plugins/Trader/locales/qya-AA.json
 create mode 100644 packages/mask/src/plugins/Trader/locales/zh-CN.json
 create mode 100644 packages/mask/src/plugins/Trader/locales/zh-TW.json

diff --git a/.i18n-codegen.json b/.i18n-codegen.json
index 89a57479f50d..6b40857beeb0 100644
--- a/.i18n-codegen.json
+++ b/.i18n-codegen.json
@@ -216,6 +216,18 @@
                 "sourceMap": "inline"
             }
         },
+        {
+            "input": "./packages/mask/src/plugins/RedPacket/locales/en-US.json",
+            "output": "./packages/mask/src/plugins/RedPacket/locales/i18n_generated",
+            "parser": { "type": "i18next", "contextSeparator": "$", "pluralSeparator": "_" },
+            "generator": {
+                "type": "i18next/react-hooks",
+                "hooks": "useI18N",
+                "namespace": "com.maskbook.red_packet",
+                "trans": "Translate",
+                "sourceMap": "inline"
+            }
+        },
         {
             "input": "./packages/mask/src/plugins/Tips/locales/en-US.json",
             "output": "./packages/mask/src/plugins/Tips/locales/i18n_generated",
@@ -239,6 +251,30 @@
                 "trans": "Translate",
                 "sourceMap": "inline"
             }
+        },
+        {
+            "input": "./packages/mask/src/plugins/Trader/locales/en-US.json",
+            "output": "./packages/mask/src/plugins/Trader/locales/i18n_generated",
+            "parser": { "type": "i18next", "contextSeparator": "$", "pluralSeparator": "_" },
+            "generator": {
+                "type": "i18next/react-hooks",
+                "hooks": "useI18N",
+                "namespace": "com.maskbook.trader",
+                "trans": "Translate",
+                "sourceMap": "inline"
+            }
+        },
+        {
+            "input": "./packages/mask/src/plugins/Gitcoin/locales/en-US.json",
+            "output": "./packages/mask/src/plugins/Gitcoin/locales/i18n_generated",
+            "parser": { "type": "i18next", "contextSeparator": "$", "pluralSeparator": "_" },
+            "generator": {
+                "type": "i18next/react-hooks",
+                "hooks": "useI18N",
+                "namespace": "com.maskbook.gitcoin",
+                "trans": "Translate",
+                "sourceMap": "inline"
+            }
         }
     ]
 }
diff --git a/packages/mask/shared-ui/locales/en-US.json b/packages/mask/shared-ui/locales/en-US.json
index 165266633b7a..5852e1f06d72 100644
--- a/packages/mask/shared-ui/locales/en-US.json
+++ b/packages/mask/shared-ui/locales/en-US.json
@@ -1,4 +1,11 @@
 {
+    "promote_red_packet": "🧧🧧🧧 Try sending Lucky Drop to your friends with tokens or NFTs to share the joy now! Install Mask.io to send your first Lucky Drop.",
+    "promote_ito": "Launch decentralized assets freely and participate in token launch directly on Twitter! Install Mask.io to participate in your first token launch activity.",
+    "promote_ito_short": "Install Mask.io to launch dencentralized assests freely on Twitter!",
+    "promote_file_service": "📃📃📃 Try to permanently use decentralized file storage on Twitter. Install Mask.io to upload and share first permanent decentralized file, powered by mainstream decentralized storage solutions.",
+    "promote_savings": "Hi friends, I just deposit {{amount}} {{symbol}} on {{chain}}. Follow @{{account}} to find more staking projects.",
+    "promote_collectible": "Display collectibles in variable NFT marketplaces, make an offer directly on social media. Install Mask.io to try to browse NFTs directly on Twitter.",
+    "promote_snapshot": "Want to actively participate in proposal vote and move projects forward? Install Mask.io to vote proposals directly on Twitter.",
     "database_backup": "Backup Database",
     "database_overwrite": "Overwrite Database with backup",
     "database_clear": "Clear Database",
@@ -46,9 +53,13 @@
     "application_settings_tab_plug_app-listed-placeholder": "Click the application icon to hide in the APP list.",
     "application_settings_tab_plug_app-unlisted-placeholder": "Click the application icon to list it on the App board.",
     "additional_post_box__encrypted_post_pre": "Decrypt this post with #mask_io ! {{encrypted}}",
-    "additional_post_box__encrypted_post_pre_red_packet_twitter_official_account": "Hi, claim this lucky drop with #mask_io @{{account}} {{encrypted}}",
-    "additional_post_box__encrypted_post_pre_red_packet": "Hi, claim this lucky drop with #mask_io {{encrypted}}",
-    "additional_post_box__steganography_post_pre": "This image is encrypted with #mask_io. 📪🔑 Install mask.io to decrypt it. {{random}}",
+    "additional_post_box__encrypted_post_pre_red_packet_twitter_official_account": "Hi, claim this lucky drop with #mask_io @{{account}} \n\n$t(promote_red_packet)\n {{encrypted}}",
+    "additional_post_box__encrypted_post_pre_red_packet": "Hi, claim this lucky drop with #mask_io\n$t(promote_red_packet) {{encrypted}}",
+    "additional_post_box__encrypted_post_pre_ito_twitter_official_account": "$t(promote_ito)\n {{encrypted}}",
+    "additional_post_box__encrypted_post_pre_ito": "$t(promote_ito) {{encrypted}}",
+    "additional_post_box__encrypted_post_pre_file_service_twitter_official_account": "$t(promote_file_service)\n {{encrypted}}",
+    "additional_post_box__encrypted_post_pre_file_service": "$t(promote_file_service) {{encrypted}}",
+    "additional_post_box__steganography_post_pre": "This image is encrypted with #mask_io.\n{{random}}\n$t(promote_red_packet)\n",
     "auto_paste_failed_dialog_title": "Paste manually",
     "auto_paste_failed_dialog_content": "Please copy the following text and image (if there is any) and publish it.",
     "auto_paste_failed_dialog_image_caption": "Open in a new tab",
@@ -133,6 +144,9 @@
     "user_guide_tip_2": "Connect and switch your wallet.",
     "user_guide_tip_3": "Browse the Web 3 footprints of your Twitter friends, enjoy the freedom of Web 3.",
     "user_guide_tip_4": "Click here to have a quick start.",
+    "plugin_avatar_setup_share_title": "NFT PFP",
+    "plugin_avatar_setup_pfp_share": "I just set my NFT PFP using Mask Extension for free! To browse other's unique NFT collections and web3 activities on Twitter, download the most powerful tool mask.io.",
+    "plugin_avatar_setup_success": "Set NFT PFP successfully.",
     "mask_network": "Mask Network",
     "import": "Import",
     "no_search_result": "No result",
@@ -162,7 +176,6 @@
     "reset": "Reset",
     "editor": "Editor",
     "retry": "Retry",
-    "search": "Search",
     "go_wrong": "Something went wrong.",
     "search_box_placeholder": "Type here to search",
     "select_all": "Select All",
@@ -185,9 +198,6 @@
     "wallet_load_retry": "Failed to load {{symbol}}. Click to retry.",
     "wallet_name": "Wallet Name",
     "wallet_rename": "Rename Wallet",
-    "wallet_add_nft_invalid_owner": "The collectible does not belong to you.",
-    "wallet_add_nft_already_added": "The collectible has already been added.",
-    "wallet_loading_token": "Loading token...",
     "wallet_loading_nft_contract": "Loading NFT contract...",
     "wallet_search_contract_no_result": "No results or contract address does not meet the query criteria.",
     "wallet_search_no_result": "No results.",
@@ -268,7 +278,6 @@
     "relative_time_months_ago": "{{months}} months ago",
     "relative_time_seconds_ago": "{{seconds}} seconds ago",
     "relative_time_years_ago": "{{years}} years ago",
-    "plugin_chain_not_supported": "Not supported on {{chain}} yet.",
     "plugin_wallet_snackbar_wait_for_confirming": "Confirm this transaction in your wallet",
     "plugin_wallet_snackbar_hash": "Transaction Submitted",
     "plugin_wallet_snackbar_confirmed": "Transaction Confirmed",
@@ -284,8 +293,6 @@
     "plugin_wallet_connecting_with": "Connecting with",
     "plugin_wallet_metamask_error_already_request": "You've opened the popup, please confirm.",
     "plugin_wallet_connect_tip": "Please make sure that your {{providerName}} wallet is provided by the offical <providerLink>{{providerShortenLink}}</providerLink>.",
-    "plugin_wallet_collections": "Collections",
-    "plugin_wallet_select_a_token": "Select a Token",
     "plugin_wallet_select_a_nft_contract": "Select an NFT Contract",
     "plugin_wallet_select_a_nft_owner": "Select an NFT Contract Owner",
     "plugin_wallet_select_a_nft_operator": "Select an NFT Contract Operator",
@@ -337,93 +344,14 @@
     "plugin_wallet_name_placeholder": "Enter 1-12 characters",
     "plugin_wallet_cancel_sign": "Signature canceled.",
     "plugin_nft_avatar_recommend_feature_description": "Set your NFT as profile picture with exclusive aura",
-    "plugin_red_packet_display_name": "Plugin: Lucky Drop",
     "application_hint": "Socialize and show off your NFTs. People can bid,buy, view your valuable NFTs without leaving Twitter.",
-    "plugin_red_packet_claimed": "Claimed",
-    "plugin_red_packet_erc20_tab_title": "Token",
-    "plugin_red_packet_erc721_tab_title": "Collectibles",
-    "plugin_red_packet_name": "Lucky Drop",
-    "plugin_red_packet_recommend_feature_description": "Astonish your friends with a surprise gift",
-    "plugin_red_packet_description": "Gift crypto or NFTs to any users, first come, first served.",
-    "plugin_red_packet_erc721_insufficient_balance": "Insufficient Balance",
-    "plugin_red_packet_details": "Lucky Drop Details",
-    "plugin_red_packet_split_mode": "Split Mode",
-    "plugin_red_packet_average": "Average",
-    "plugin_red_packet_random": "Random",
-    "plugin_red_packet_shares": "Shares",
-    "plugin_red_packet_best_wishes": "Best Wishes!",
-    "plugin_red_packet_create_new": "New",
-    "plugin_red_packet_claim": "Claim",
-    "plugin_red_packet_claiming": "Claiming...",
-    "plugin_red_packet_refund": "Refund",
-    "plugin_red_packet_empty": "Empty",
-    "plugin_red_packet_data_broken": "The Lucky Drop can’t be sent due to data damage. Please withdraw the assets after {{duration}}.",
-    "plugin_red_packet_refunding": "Refunding",
-    "plugin_red_packet_select_existing": "Past",
-    "plugin_red_packet_share_unclaimed_message_official_account": "Hi friends, I just found a lucky drop sent by @{{sender}} on {{network}} network. Follow @{{account}} (mask.io) to claim lucky drops.\n#mask_io #LuckyDrop\n{{payload}}",
-    "plugin_red_packet_share_unclaimed_message_not_twitter": "Hi friends, I just found a lucky drop sent by @{{sender}} on {{network}} network. \n{{payload}}",
-    "plugin_red_packet_share_message_official_account": "I just claimed a lucky drop from @{{sender}} on {{network}} network. Follow @{{account}} (mask.io) to claim lucky drops.\n#mask_io #LuckyDrop\n{{payload}}",
-    "plugin_red_packet_share_message_not_twitter": "I just claimed a lucky drop from @{{sender}} on {{network}} network. \n{{payload}}",
-    "plugin_red_packet_nft_share_foreshow_message": "@{{sender}} is sending an NFT lucky drop on {{network}} network. Follow @{{account}} (mask.io) to claim NFT lucky drops.\n#mask_io #LuckyDrop\n{{payload}}",
-    "plugin_red_packet_nft_share_foreshow_message_not_twitter": "@{{sender}} is sending an NFT lucky drop on {{network}} network. \n{{payload}}",
-    "plugin_red_packet_nft_share_claimed_message": "I just claimed an NFT lucky drop from @{{sender}} on {{network}} network. Follow @{{account}} (mask.io) to claim NFT lucky drops.\n#mask_io #LuckyDrop\n{{payload}}",
-    "plugin_red_packet_nft_share_claimed_message_not_twitter": "I just claimed an NFT lucky drop from @{{sender}} on {{network}} network. \n{{payload}}",
-    "plugin_red_packet_nft_tip": "This is an NFT lucky drop.",
-    "plugin_red_packet_nft_no_history": "You haven’t created any NFT lucky drop yet. Try to create and share lucky with your friends.",
-    "plugin_red_packet_attached_message": "Attached Message",
-    "plugin_red_packet_from": "From: @{{name}}",
-    "plugin_red_packet_description_claimed": "You got {{amount}} {{symbol}}",
-    "plugin_red_packet_description_expired": "The Lucky Drop is expired.",
-    "plugin_red_packet_description_refunded": "The Lucky Drop has been refunded.",
-    "plugin_red_packet_description_refund": "You could refund {{balance}} {{symbol}}.",
-    "plugin_red_packet_description_empty": "The Lucky Drop is empty.",
-    "plugin_red_packet_description_broken": "The Lucky Drop is broken.",
-    "plugin_red_packet_description_failover": "{{shares}} shares / {{total}} {{symbol}}",
-    "plugin_red_packet_amount_per_share": "Amount per Share",
-    "plugin_red_packet_send_symbol": "Send {{amount}} {{symbol}}",
-    "plugin_red_packet_amount_total": "Amount Total",
-    "plugin_red_packet_next": "Next",
-    "plugin_red_packet_back": "Back",
-    "plugin_red_packet_hint": "You can withdraw the remaining balance 24 hours after the Lucky Drop is sent.",
-    "plugin_red_packet_token": "Token",
-    "plugin_red_packet_message_label": "Title",
     "plugin_red_packet_create": "Create a Lucky Drop",
     "plugin_red_packet_create_with_token": "Create a Lucky Drop with {{amount}} {{symbol}}",
-    "plugin_red_packet_history_duration": "Time: {{startTime}} ~ {{endTime}} (UTC+8)",
-    "plugin_red_packet_history_total_amount": "Total Amount: {{amount}} {{symbol}}",
-    "plugin_red_packet_history_total_claimed_amount": "Total: <span><strong>{{claimedAmount}}/{{amount}}</strong></span> {{symbol}}",
-    "plugin_red_packet_history_claimed": "Claimed: <strong>{{claimedShares}}/{{shares}}</strong> Share",
-    "plugin_red_packet_history_split_mode": "Split Mode: {{mode}}",
-    "plugin_red_packet_history_send": "Send",
     "plugin_nft_red_packet_create": "Create an NFT Lucky Drop",
     "plugin_red_packet_nft_account_name": "Wallet account",
-    "plugin_red_packet_nft_attached_message": "Attached Message",
-    "plugin_red_packet_nft_total_amount": "Total Amount",
-    "plugin_red_packet_nft_select_collection": "Choose your collection",
-    "plugin_red_packet_nft_max_shares": "The maximum number of NFTs to be sold in NFT lucky drop contract is {{amount}}.",
-    "plugin_red_packet_nft_max_shares_tip": "The NFT lucky drop supports up to {{amount}} NFTs selected for one time.",
     "plugin_red_packet_nft_shift_select_tip": "You can also use <text>{{text}}</text> to select multiple NFTs.",
-    "plugin_red_packet_nft_non_existed_tip": "Token ID <tokenIdList></tokenIdList> does not exist or belong to you.",
-    "plugin_red_packet_nft_select_all_option": "ALL ({{total}} NFT)",
-    "plugin_red_packet_nft_select_partially_option": "Select partially",
-    "plugin_red_packet_nft_unapproved_tip": "You can get the rest of your NFTs back after 24 hours of sending.",
-    "plugin_red_packet_nft_approve_all_tip": "Note: When selecting approve all, all NFTs in the contract will be authorized for sale by default, including the NFTs transfered later.",
-    "plugin_red_packet_completed": "Completed",
-    "plugin_red_packet_expired": "Expired",
-    "plugin_red_packet_indivisible": "The minimum amount for each share is {{amount}} {{symbol}}",
-    "plugin_nft_red_packet_data_broken": "The Lucky Drop can’t be sent due to data damage.",
-    "plugin_gitcoin_readme": "By using this service, you will also be contributing 5% of your contribution to the <fund>Gitcoin grants development fund</fund>.",
-    "plugin_gitcoin_readme_fund_link": "https://gitcoin.co/grants/86/gitcoin-sustainability-fund",
     "plugin_gitcoin_select_a_token": "Select a token",
-    "plugin_gitcoin_name": "Gitcoin",
-    "plugin_gitcoin_description": "Display specific information of Gitcoin projects, donate to a project directly on social media.",
-    "plugin_gitcoin_enter_an_amount": "Enter an amount",
-    "plugin_gitcoin_grant_not_available": "Grant not available",
     "plugin_gitcoin_insufficient_balance": "Insufficient {{symbol}} balance",
-    "plugin_gitcoin_donate": "Donate",
-    "plugin_gitcoin_last_updated": "Last update:",
-    "plugin_gitcoin_by": "By",
-    "plugin_gitcoin_view_on": "View on Gitcoin",
     "plugin_trader_fail_to_load": "Fail to load trending info from  ",
     "plugin_trader_lbp_pool_in_balancer": "LBP Pool in Balancer",
     "plugin_trader_swap_description": "Pop-up trading widget allows you to instantly view prices of the hottest Crypto/Stock and trade, also invest in the best performing managers.",
@@ -612,10 +540,10 @@
     "plugin_ito_qualification_failed": "The wallet address isn't on the list.",
     "plugin_ito_congratulations": "Congratulations!",
     "plugin_ito_out_of_stock_hit": "Better luck next time",
-    "plugin_ito_claim_success_share": "I just attended @{{user}}'s #ITO with @{{account}} to swap ${{symbol}}. Install mask.io and start your own Initial Twitter Offering! \n {{link}}",
-    "plugin_ito_claim_success_share_no_official_account": "I just attended @{{user}}'s #ITO to swap ${{symbol}}. Install mask.io and start your own Initial Twitter Offering! \n {{link}}",
-    "plugin_ito_claim_foreshow_share": "{{symbol}}({{name}}) is launching a new #ITO. Come and join it! Follow @{{account}} (mask.io) to find more events! \n {{link}}",
-    "plugin_ito_claim_foreshow_share_no_official_account": "{{symbol}}({{name}}) is launching a new #ITO. Come and join it! \n {{link}}",
+    "plugin_ito_claim_success_share": "I just attended @{{user}}'s #ITO with @{{account}} to swap ${{symbol}}.\n$t(promote_ito_short)\n{{link}}",
+    "plugin_ito_claim_success_share_no_official_account": "I just attended @{{user}}'s #ITO to swap ${{symbol}}.\n$t(promote_ito_short)\n{{link}}",
+    "plugin_ito_claim_foreshow_share": "{{symbol}}({{name}}) is launching a new #ITO. Come and join it! Follow @{{account}} (mask.io) to find more events! \n {{link}}\n$t(promote_ito_short)\n",
+    "plugin_ito_claim_foreshow_share_no_official_account": "{{symbol}}({{name}}) is launching a new #ITO. Come and join it! \n {{link}}\n$t(promote_ito_short)\n",
     "plugin_ito_password": "Password: {{password}}",
     "plugin_ito_status_no_start": "Not started",
     "plugin_ito_status_ongoing": "Ongoing",
diff --git a/packages/mask/shared-ui/locales/ja-JP.json b/packages/mask/shared-ui/locales/ja-JP.json
index 393a7e0e4ef9..118edbbc103a 100644
--- a/packages/mask/shared-ui/locales/ja-JP.json
+++ b/packages/mask/shared-ui/locales/ja-JP.json
@@ -47,7 +47,6 @@
     "post_modal_hint__button": "投稿を暗号化",
     "hide": "隠す",
     "reset": "リセットする",
-    "search": "検索",
     "search_box_placeholder": "ここに打って検索",
     "select_all": "全て選ぶ",
     "select_none": "何も選んでいません。",
@@ -77,7 +76,6 @@
     "relative_time_months_ago": "{{months}}月前",
     "relative_time_seconds_ago": "{{seconds}}秒前",
     "relative_time_years_ago": "{{years}}年前",
-    "plugin_wallet_select_a_token": "トークンを選択",
     "plugin_wallet_connect_a_wallet": "ウォレットと接続",
     "plugin_wallet_no_gas_fee": "ガス代が不足しています",
     "plugin_wallet_update_gas_fee": "ガス代を更新中…",
@@ -105,26 +103,6 @@
     "plugin_wallet_on_connect_in_firefox": "接続する",
     "plugin_wallet_return_mobile_wallet_options": "モバイルウォレットのオプションに戻る",
     "plugin_wallet_view_qr_code": "QR コードを表示する",
-    "plugin_red_packet_display_name": "プラグイン: 投げ銭",
-    "plugin_red_packet_split_mode": "分割モード",
-    "plugin_red_packet_average": "平均",
-    "plugin_red_packet_random": "ランダム",
-    "plugin_red_packet_shares": "株数",
-    "plugin_red_packet_best_wishes": "幸運を祈ります！",
-    "plugin_red_packet_create_new": "新しい投げ銭を作成",
-    "plugin_red_packet_claim": "受け取る",
-    "plugin_red_packet_refund": "無効",
-    "plugin_red_packet_select_existing": "既存の投げ銭を選択",
-    "plugin_red_packet_attached_message": "メッセージを追加",
-    "plugin_red_packet_from": "送信者：{{name}}",
-    "plugin_red_packet_description_expired": "期限切れです",
-    "plugin_red_packet_description_refunded": "無効です",
-    "plugin_red_packet_description_refund": "{{balance}}{{symbol}}を受け取れます",
-    "plugin_red_packet_description_empty": "期限切れです",
-    "plugin_red_packet_description_broken": "破損しています",
-    "plugin_red_packet_description_failover": "{{amount}} {{symbol}} / {{shares}} 株",
-    "plugin_red_packet_amount_per_share": "1株当たりの金額",
-    "plugin_red_packet_send_symbol": "{{symbol}} を送る",
     "plugin_red_packet_create_with_token": "{{symbol}} で投げ銭を作成する",
     "plugin_gitcoin_readme": "このサービスを利用するにあたり、寄付金額の 5% が <fund>Gitcoin grants development fund</fund> に寄付されます",
     "plugin_gitcoin_select_a_token": "トークンを選択する",
diff --git a/packages/mask/shared-ui/locales/ko-KR.json b/packages/mask/shared-ui/locales/ko-KR.json
index 1720ce293397..b69cc4e22da5 100644
--- a/packages/mask/shared-ui/locales/ko-KR.json
+++ b/packages/mask/shared-ui/locales/ko-KR.json
@@ -98,7 +98,6 @@
     "reset": "초기화",
     "editor": "에디터",
     "retry": "다시 시도",
-    "search": "검색",
     "go_wrong": "오류가 발생합니다.",
     "search_box_placeholder": "여기서 검색하기",
     "select_all": "전체 선택",
@@ -118,12 +117,8 @@
     "transfer": "이체",
     "wallet_name": "월렛 이름",
     "wallet_rename": "월렛 이름 바꾸기",
-    "wallet_add_nft_invalid_owner": "해당 수집품은 존재하지 않거나 유저님의 것이 아닙니다.",
-    "wallet_add_nft_already_added": "이미 추가된 수집품입니다.",
-    "wallet_loading_token": "토큰 로딩...",
     "wallet_loading_nft_contract": "NFT 컨트렉트 로딩...",
     "wallet_search_contract_no_result": "검색 포준에 맞추는 결과나 컨트렉트 주소가 없습니다.",
-    "wallet_search_no_result": "결과 없음.",
     "wallet_airdrop_nft_unclaimed_title": "수령하지 않는 NFT 에어드랍:",
     "plugin_airdrop_nft_start_time": "시작 시간: {{date}}",
     "plugin_airdrop_nft_end_time": "종료 시간: {{date}}",
@@ -171,8 +166,6 @@
     "relative_time_months_ago": "{{months}} 월 전",
     "relative_time_seconds_ago": "{{seconds}} 초 전",
     "relative_time_years_ago": "{{years}} 년 전",
-    "plugin_wallet_collections": "컬렉션",
-    "plugin_wallet_select_a_token": "토큰을 선택하기",
     "plugin_wallet_select_a_nft_contract": "NFT 컨트렉트를 선택하세요.",
     "plugin_wallet_select_a_nft_owner": "NFT 컨트렉트 소유자를 선택하세요.",
     "plugin_wallet_select_a_nft_operator": "NFT 컨트렉트 오퍼레이터",
@@ -219,61 +212,8 @@
     "plugin_wallet_connect_wallet": "월렛 연결",
     "plugin_wallet_name_placeholder": "1-12 자 입력하세요",
     "plugin_wallet_cancel_sign": "서명이 실패되었습니다.",
-    "plugin_red_packet_display_name": "플러그인: 빨간 백",
-    "plugin_red_packet_erc20_tab_title": "토큰",
-    "plugin_red_packet_erc721_tab_title": "수집품",
-    "plugin_red_packet_erc721_insufficient_balance": "잔액 부족",
-    "plugin_red_packet_details": "행운 드랍 디테일",
-    "plugin_red_packet_split_mode": "분할 모드",
-    "plugin_red_packet_average": "평균",
-    "plugin_red_packet_random": "렌덤",
-    "plugin_red_packet_shares": "공유",
-    "plugin_red_packet_best_wishes": "행운을 빌어요!",
-    "plugin_red_packet_create_new": "새로운 것 만들기",
-    "plugin_red_packet_claim": "수령",
-    "plugin_red_packet_claiming": "수령 중...",
-    "plugin_red_packet_refund": "반환",
-    "plugin_red_packet_empty": "비어 있음",
-    "plugin_red_packet_data_broken": "데이터 손상으로 인해 레드 패킷을 보낼 수 없습니다. {{duration}} 후 자산을 인출할 수 있습니다.",
-    "plugin_red_packet_refunding": "환급 중",
-    "plugin_red_packet_select_existing": "기존 빨간 백 선택",
-    "plugin_red_packet_share_message_official_account": "{{network}} 네트워크에서 @{{sender}}의 레트 패킷을 받았어요. @{{account}} (mask.io) 팔로우하고 레드 패킷을 받으세요.\n#mask_io #LuckyDrop\n{{payload}}",
-    "plugin_red_packet_share_message_not_twitter": "{{network}} 에서 @{{sender}} 의 레드 패킷을 클레임했습니다.\n{{payload}}",
-    "plugin_red_packet_nft_share_foreshow_message": "@{{network}} 네트워크에서 @{{sender}} 레트 패킷을 보내고 있습니다. @{{account}} (mask.io) 팔로우하고 NFT 레드 패킷을 받으세요.\n#mask_io #LuckyDrop\n{{payload}}",
-    "plugin_red_packet_nft_share_foreshow_message_not_twitter": "@{{network}} 네트워크에서 @{{sender}} NFT 레트 패킷을 보내고 있습니다. \n{{payload}}",
-    "plugin_red_packet_nft_share_claimed_message": "{{network}} 네트워크에서 @{{sender}}의 NFT 레트 패킷을 받았어요. @{{account}} (mask.io) 팔로우하고 NFT 레드 패킷을 받으세요.\n#mask_io #LuckyDrop\n{{payload}}",
-    "plugin_red_packet_nft_share_claimed_message_not_twitter": "{{network}} 에서 @{{sender}} 의 NFT 레드 패킷을 클레임했습니다.\n{{payload}}",
-    "plugin_red_packet_nft_tip": "이것은 NFT 레드 패킷입니다.",
-    "plugin_red_packet_attached_message": "첨부 메시지",
-    "plugin_red_packet_from": "보낸사람: {{name}}",
-    "plugin_red_packet_description_claimed": "이미 수령하였습니다.",
-    "plugin_red_packet_description_expired": "이 빨간 백 이미 만료되었습니다.",
-    "plugin_red_packet_description_refunded": "이 빨간 백 이미 반환되었습니다.",
-    "plugin_red_packet_description_refund": "{{balance}} {{symbol} 반환 가능합니다.",
-    "plugin_red_packet_description_empty": "빈 빨간 백입니다.",
-    "plugin_red_packet_description_broken": "파손된 빨간 백입니다.",
-    "plugin_red_packet_description_failover": "{{name}} / {{shares}}에서  / {{total}} {{symbol}} 공유합니다.",
-    "plugin_red_packet_amount_per_share": "공유 총액",
-    "plugin_red_packet_send_symbol": "{{symbol}} 보내기",
-    "plugin_red_packet_amount_total": "총액",
-    "plugin_red_packet_next": "다음",
-    "plugin_red_packet_back": "뒤로",
-    "plugin_red_packet_hint": "레드 패킷이 공유된 후 24시간 후에 나머지 잔액을 인출할 수 있습니다.",
-    "plugin_red_packet_token": "토큰",
-    "plugin_red_packet_message_label": "제목",
     "plugin_red_packet_create_with_token": "{{symbol}} 으로 빨간 백 만드는 중",
-    "plugin_red_packet_history_duration": "시간: {{startTime}} ~ {{endTime}} (UTC+8)",
-    "plugin_red_packet_history_total_amount": "총액: {{amount}} {{symbol}}",
-    "plugin_red_packet_history_total_claimed_amount": "전체: <span><strong>{{claimedAmount}}/{{amount}}</strong></span> {{symbol}}",
-    "plugin_red_packet_history_claimed": "수령됨: <strong>{{claimedShares}}/{{shares}}</strong> 공유",
-    "plugin_red_packet_history_split_mode": "Split Mode: {{mode}}",
-    "plugin_red_packet_history_send": "발송",
     "plugin_red_packet_nft_account_name": "지갑 계정",
-    "plugin_red_packet_nft_attached_message": "첨부 메시지",
-    "plugin_red_packet_nft_total_amount": "총 금액",
-    "plugin_red_packet_completed": "완료됨",
-    "plugin_red_packet_expired": "만료됨",
-    "plugin_nft_red_packet_data_broken": "테이터 훼손때문에 해당 레드 패킷을 보낼 수 없습니다.",
     "plugin_gitcoin_readme": "이 서비스를 이용하면 <fund>Gitcoin 개발 기금에 당신의 기여금</fund>의 5%를 기부할 것이다.",
     "plugin_gitcoin_readme_fund_link": "https://gitcoin.co/grants/86/gitcoin-sustainability-fund",
     "plugin_gitcoin_select_a_token": "토큰 선택",
diff --git a/packages/mask/shared-ui/locales/qya-AA.json b/packages/mask/shared-ui/locales/qya-AA.json
index f70c67a90739..ecb471a7d0e4 100644
--- a/packages/mask/shared-ui/locales/qya-AA.json
+++ b/packages/mask/shared-ui/locales/qya-AA.json
@@ -162,7 +162,6 @@
     "reset": "crwdns4301:0crwdne4301:0",
     "editor": "crwdns10131:0crwdne10131:0",
     "retry": "crwdns4303:0crwdne4303:0",
-    "search": "crwdns4327:0crwdne4327:0",
     "go_wrong": "crwdns10133:0crwdne10133:0",
     "search_box_placeholder": "crwdns4331:0crwdne4331:0",
     "select_all": "crwdns4333:0crwdne4333:0",
@@ -185,12 +184,8 @@
     "wallet_load_retry": "crwdns10135:0{{symbol}}crwdne10135:0",
     "wallet_name": "crwdns4487:0crwdne4487:0",
     "wallet_rename": "crwdns4489:0crwdne4489:0",
-    "wallet_add_nft_invalid_owner": "crwdns7723:0crwdne7723:0",
-    "wallet_add_nft_already_added": "crwdns8131:0crwdne8131:0",
-    "wallet_loading_token": "crwdns8133:0crwdne8133:0",
     "wallet_loading_nft_contract": "crwdns8135:0crwdne8135:0",
     "wallet_search_contract_no_result": "crwdns8137:0crwdne8137:0",
-    "wallet_search_no_result": "crwdns8139:0crwdne8139:0",
     "wallet_confirm_with_password": "crwdns10141:0crwdne10141:0",
     "wallet_airdrop_nft_unclaimed_title": "crwdns8093:0crwdne8093:0",
     "plugin_external_unknown_plugin": "crwdns10143:0crwdne10143:0",
@@ -268,7 +263,6 @@
     "relative_time_months_ago": "crwdns4581:0{{months}}crwdne4581:0",
     "relative_time_seconds_ago": "crwdns4583:0{{seconds}}crwdne4583:0",
     "relative_time_years_ago": "crwdns4585:0{{years}}crwdne4585:0",
-    "plugin_chain_not_supported": "crwdns10689:0{{chain}}crwdne10689:0",
     "plugin_wallet_snackbar_wait_for_confirming": "crwdns10629:0crwdne10629:0",
     "plugin_wallet_snackbar_hash": "crwdns10631:0crwdne10631:0",
     "plugin_wallet_snackbar_confirmed": "crwdns10633:0crwdne10633:0",
@@ -284,8 +278,6 @@
     "plugin_wallet_connecting_with": "crwdns14612:0crwdne14612:0",
     "plugin_wallet_metamask_error_already_request": "crwdns12987:0crwdne12987:0",
     "plugin_wallet_connect_tip": "crwdns12989:0{{providerName}}crwdnd12989:0{{providerShortenLink}}crwdne12989:0",
-    "plugin_wallet_collections": "crwdns8141:0crwdne8141:0",
-    "plugin_wallet_select_a_token": "crwdns4589:0crwdne4589:0",
     "plugin_wallet_select_a_nft_contract": "crwdns7923:0crwdne7923:0",
     "plugin_wallet_select_a_nft_owner": "crwdns7925:0crwdne7925:0",
     "plugin_wallet_select_a_nft_operator": "crwdns7927:0crwdne7927:0",
@@ -337,81 +329,12 @@
     "plugin_wallet_name_placeholder": "crwdns4705:0crwdne4705:0",
     "plugin_wallet_cancel_sign": "crwdns4709:0crwdne4709:0",
     "plugin_nft_avatar_recommend_feature_description": "crwdns16626:0crwdne16626:0",
-    "plugin_red_packet_display_name": "crwdns4711:0crwdne4711:0",
     "application_hint": "crwdns16628:0crwdne16628:0",
-    "plugin_red_packet_claimed": "crwdns10205:0crwdne10205:0",
-    "plugin_red_packet_erc20_tab_title": "crwdns8143:0crwdne8143:0",
-    "plugin_red_packet_erc721_tab_title": "crwdns8145:0crwdne8145:0",
-    "plugin_red_packet_name": "crwdns16234:0crwdne16234:0",
-    "plugin_red_packet_recommend_feature_description": "crwdns16544:0crwdne16544:0",
-    "plugin_red_packet_description": "crwdns16236:0crwdne16236:0",
-    "plugin_red_packet_erc721_insufficient_balance": "crwdns8147:0crwdne8147:0",
-    "plugin_red_packet_details": "crwdns9071:0crwdne9071:0",
-    "plugin_red_packet_split_mode": "crwdns4713:0crwdne4713:0",
-    "plugin_red_packet_average": "crwdns4715:0crwdne4715:0",
-    "plugin_red_packet_random": "crwdns4717:0crwdne4717:0",
-    "plugin_red_packet_shares": "crwdns4719:0crwdne4719:0",
-    "plugin_red_packet_best_wishes": "crwdns4721:0crwdne4721:0",
-    "plugin_red_packet_create_new": "crwdns4723:0crwdne4723:0",
-    "plugin_red_packet_claim": "crwdns4725:0crwdne4725:0",
-    "plugin_red_packet_claiming": "crwdns4727:0crwdne4727:0",
-    "plugin_red_packet_refund": "crwdns4729:0crwdne4729:0",
-    "plugin_red_packet_empty": "crwdns4731:0crwdne4731:0",
-    "plugin_red_packet_data_broken": "crwdns7999:0{{duration}}crwdne7999:0",
-    "plugin_red_packet_refunding": "crwdns4733:0crwdne4733:0",
-    "plugin_red_packet_select_existing": "crwdns4735:0crwdne4735:0",
-    "plugin_red_packet_share_unclaimed_message_official_account": "crwdns14614:0{{sender}}crwdnd14614:0{{network}}crwdnd14614:0{{account}}crwdnd14614:0{{payload}}crwdne14614:0",
-    "plugin_red_packet_share_unclaimed_message_not_twitter": "crwdns14616:0{{sender}}crwdnd14616:0{{network}}crwdnd14616:0{{payload}}crwdne14616:0",
-    "plugin_red_packet_share_message_official_account": "crwdns9175:0{{sender}}crwdnd9175:0{{network}}crwdnd9175:0{{account}}crwdnd9175:0{{payload}}crwdne9175:0",
-    "plugin_red_packet_share_message_not_twitter": "crwdns9177:0{{sender}}crwdnd9177:0{{network}}crwdnd9177:0{{payload}}crwdne9177:0",
-    "plugin_red_packet_nft_share_foreshow_message": "crwdns8151:0{{sender}}crwdnd8151:0{{network}}crwdnd8151:0{{account}}crwdnd8151:0{{payload}}crwdne8151:0",
-    "plugin_red_packet_nft_share_foreshow_message_not_twitter": "crwdns9011:0{{sender}}crwdnd9011:0{{network}}crwdnd9011:0{{payload}}crwdne9011:0",
-    "plugin_red_packet_nft_share_claimed_message": "crwdns8153:0{{sender}}crwdnd8153:0{{network}}crwdnd8153:0{{account}}crwdnd8153:0{{payload}}crwdne8153:0",
-    "plugin_red_packet_nft_share_claimed_message_not_twitter": "crwdns9013:0{{sender}}crwdnd9013:0{{network}}crwdnd9013:0{{payload}}crwdne9013:0",
-    "plugin_red_packet_nft_tip": "crwdns8155:0crwdne8155:0",
-    "plugin_red_packet_nft_no_history": "crwdns12991:0crwdne12991:0",
-    "plugin_red_packet_attached_message": "crwdns4739:0crwdne4739:0",
-    "plugin_red_packet_from": "crwdns4741:0{{name}}crwdne4741:0",
-    "plugin_red_packet_description_claimed": "crwdns4743:0{{amount}}crwdnd4743:0{{symbol}}crwdne4743:0",
-    "plugin_red_packet_description_expired": "crwdns4745:0crwdne4745:0",
-    "plugin_red_packet_description_refunded": "crwdns4747:0crwdne4747:0",
-    "plugin_red_packet_description_refund": "crwdns4749:0{{balance}}crwdnd4749:0{{symbol}}crwdne4749:0",
-    "plugin_red_packet_description_empty": "crwdns4751:0crwdne4751:0",
-    "plugin_red_packet_description_broken": "crwdns4753:0crwdne4753:0",
-    "plugin_red_packet_description_failover": "crwdns4755:0{{shares}}crwdnd4755:0{{total}}crwdnd4755:0{{symbol}}crwdne4755:0",
-    "plugin_red_packet_amount_per_share": "crwdns4761:0crwdne4761:0",
-    "plugin_red_packet_send_symbol": "crwdns4763:0{{amount}}crwdnd4763:0{{symbol}}crwdne4763:0",
-    "plugin_red_packet_amount_total": "crwdns4765:0crwdne4765:0",
-    "plugin_red_packet_next": "crwdns4767:0crwdne4767:0",
-    "plugin_red_packet_back": "crwdns4769:0crwdne4769:0",
-    "plugin_red_packet_hint": "crwdns4771:0crwdne4771:0",
-    "plugin_red_packet_token": "crwdns4773:0crwdne4773:0",
-    "plugin_red_packet_message_label": "crwdns8157:0crwdne8157:0",
     "plugin_red_packet_create": "crwdns11873:0crwdne11873:0",
     "plugin_red_packet_create_with_token": "crwdns4775:0{{amount}}crwdnd4775:0{{symbol}}crwdne4775:0",
-    "plugin_red_packet_history_duration": "crwdns4777:0{{startTime}}crwdnd4777:0{{endTime}}crwdne4777:0",
-    "plugin_red_packet_history_total_amount": "crwdns4779:0{{amount}}crwdnd4779:0{{symbol}}crwdne4779:0",
-    "plugin_red_packet_history_total_claimed_amount": "crwdns4781:0{{claimedAmount}}crwdnd4781:0{{amount}}crwdnd4781:0{{symbol}}crwdne4781:0",
-    "plugin_red_packet_history_claimed": "crwdns4783:0{{claimedShares}}crwdnd4783:0{{shares}}crwdne4783:0",
-    "plugin_red_packet_history_split_mode": "crwdns4785:0{{mode}}crwdne4785:0",
-    "plugin_red_packet_history_send": "crwdns4787:0crwdne4787:0",
     "plugin_nft_red_packet_create": "crwdns11875:0crwdne11875:0",
     "plugin_red_packet_nft_account_name": "crwdns8159:0crwdne8159:0",
-    "plugin_red_packet_nft_attached_message": "crwdns8161:0crwdne8161:0",
-    "plugin_red_packet_nft_total_amount": "crwdns8163:0crwdne8163:0",
-    "plugin_red_packet_nft_select_collection": "crwdns10813:0crwdne10813:0",
-    "plugin_red_packet_nft_max_shares": "crwdns10815:0{{amount}}crwdne10815:0",
-    "plugin_red_packet_nft_max_shares_tip": "crwdns10817:0{{amount}}crwdne10817:0",
     "plugin_red_packet_nft_shift_select_tip": "crwdns10819:0{{text}}crwdne10819:0",
-    "plugin_red_packet_nft_non_existed_tip": "crwdns10821:0crwdne10821:0",
-    "plugin_red_packet_nft_select_all_option": "crwdns10823:0{{total}}crwdne10823:0",
-    "plugin_red_packet_nft_select_partially_option": "crwdns10825:0crwdne10825:0",
-    "plugin_red_packet_nft_unapproved_tip": "crwdns14622:0crwdne14622:0",
-    "plugin_red_packet_nft_approve_all_tip": "crwdns10827:0crwdne10827:0",
-    "plugin_red_packet_completed": "crwdns8165:0crwdne8165:0",
-    "plugin_red_packet_expired": "crwdns8167:0crwdne8167:0",
-    "plugin_red_packet_indivisible": "crwdns10621:0{{amount}}crwdnd10621:0{{symbol}}crwdne10621:0",
-    "plugin_nft_red_packet_data_broken": "crwdns8169:0crwdne8169:0",
     "plugin_gitcoin_readme": "crwdns4789:0crwdne4789:0",
     "plugin_gitcoin_readme_fund_link": "crwdns4791:0crwdne4791:0",
     "plugin_gitcoin_select_a_token": "crwdns4793:0crwdne4793:0",
diff --git a/packages/mask/shared-ui/locales/zh-CN.json b/packages/mask/shared-ui/locales/zh-CN.json
index 5e4435fc2eba..98ebee360881 100644
--- a/packages/mask/shared-ui/locales/zh-CN.json
+++ b/packages/mask/shared-ui/locales/zh-CN.json
@@ -134,7 +134,6 @@
     "reset": "重置",
     "editor": "编辑器",
     "retry": "重试",
-    "search": "搜索",
     "go_wrong": "出现了一些问题。",
     "search_box_placeholder": "输入要搜索的内容",
     "select_all": "全选",
@@ -156,12 +155,8 @@
     "wallet_load_retry": "加载 {{symbol}} 失败，请点击重试。",
     "wallet_name": "钱包名称",
     "wallet_rename": "重新命名钱包",
-    "wallet_add_nft_invalid_owner": "此收藏品不属于您。",
-    "wallet_add_nft_already_added": "此收藏品已被添加。",
-    "wallet_loading_token": "正在加载代币",
     "wallet_loading_nft_contract": "正在加载 NFT 合约...",
     "wallet_search_contract_no_result": "没有结果或合同地址不符合查询标准。",
-    "wallet_search_no_result": "没有任何结果。",
     "wallet_confirm_with_password": "使用密码确认",
     "wallet_airdrop_nft_unclaimed_title": "未认领的NFT Airdrop：",
     "plugin_external_unknown_plugin": "发现新的未知Mask插件，您想要加载它们吗？",
@@ -231,7 +226,6 @@
     "relative_time_months_ago": "{{months}} 个月前",
     "relative_time_seconds_ago": "{{seconds}} 秒前",
     "relative_time_years_ago": "{{years}} 年前",
-    "plugin_chain_not_supported": "尚未在 {{chain}} 上支持。",
     "plugin_wallet_snackbar_wait_for_confirming": "请在您的钱包中确认这笔交易",
     "plugin_wallet_snackbar_hash": "交易已提交",
     "plugin_wallet_snackbar_confirmed": "交易已确认",
@@ -247,8 +241,6 @@
     "plugin_wallet_connecting_with": "连接到",
     "plugin_wallet_metamask_error_already_request": "您已打开弹出窗口，请确认。",
     "plugin_wallet_connect_tip": "请确保您的 {{providerName}} 钱包是由官方 <providerLink>{{providerShortenLink}}</providerLink> 提供的。",
-    "plugin_wallet_collections": "收藏品",
-    "plugin_wallet_select_a_token": "选择代币",
     "plugin_wallet_select_a_nft_contract": "选择 NFT 合约",
     "plugin_wallet_select_a_nft_owner": "选择 NFT 合约持有者",
     "plugin_wallet_select_a_nft_operator": "选择 NFT 合约",
@@ -296,76 +288,11 @@
     "plugin_wallet_connect_wallet": "连接钱包",
     "plugin_wallet_name_placeholder": "输入1-12 个字符",
     "plugin_wallet_cancel_sign": "签名已取消。",
-    "plugin_red_packet_display_name": "插件：红包",
-    "plugin_red_packet_claimed": "已认领",
-    "plugin_red_packet_erc20_tab_title": "代币",
-    "plugin_red_packet_erc721_tab_title": "收藏品",
-    "plugin_red_packet_erc721_insufficient_balance": "余额不足",
-    "plugin_red_packet_details": "红包详情",
-    "plugin_red_packet_split_mode": "分享模式",
-    "plugin_red_packet_average": "平均",
-    "plugin_red_packet_random": "随机",
-    "plugin_red_packet_shares": "份额",
-    "plugin_red_packet_best_wishes": "祝好运！",
-    "plugin_red_packet_create_new": "新建",
-    "plugin_red_packet_claim": "认领",
-    "plugin_red_packet_claiming": "认领中...",
-    "plugin_red_packet_refund": "退款",
-    "plugin_red_packet_empty": "空的",
-    "plugin_red_packet_data_broken": "由于数据损坏，此红包无法发送。请在 {{duration}} 之后申请退款。",
-    "plugin_red_packet_refunding": "退款中",
-    "plugin_red_packet_select_existing": "历史红包",
-    "plugin_red_packet_share_unclaimed_message_official_account": "朋友们，我刚刚发现 @{{sender}} 在 {{network}} 网络发送了一个红包。 关注 @{{account}} (mask.io) 以获取红包。\n#mask_io #LuckyDrop\n{{payload}}",
-    "plugin_red_packet_share_unclaimed_message_not_twitter": "朋友们，我刚刚发现 @{{sender}} 在 {{network}} 网络发送了一个红包。\n{{payload}}",
-    "plugin_red_packet_share_message_official_account": "我刚从@{{sender}} 在 {{network}} 网络上认领了一个红包。关注@{{account}} (mask.io) 来获取红包。\n#mask_io #LuckyDrop\n{{payload}}",
-    "plugin_red_packet_share_message_not_twitter": "我刚从@{{sender}} 在 {{network}} 网络上认领了一个红包。\n{{payload}}",
-    "plugin_red_packet_nft_share_foreshow_message": "@{{sender}} 正在 {{network}} 网络上发送一个 NFT 红包。关注@{{account}} (mask.io) 以获取NFT 红包。\n#mask_io #LuckyDrop\n{{payload}}",
-    "plugin_red_packet_nft_share_foreshow_message_not_twitter": "@{{sender}} 正在 {{network}} 网络上发送一个 NFT 红包。 \n{{payload}}",
-    "plugin_red_packet_nft_share_claimed_message": "我刚从@{{sender}} 在 {{network}} 网络上认领了一个 NFT 红包。关注@{{account}} (mask.io) 来获取红包。\n#mask_io #LuckyDrop\n{{payload}}",
-    "plugin_red_packet_nft_share_claimed_message_not_twitter": "我刚从@{{sender}} 在 {{network}} 网络上认领了一个 NFT 红包。\n{{payload}}",
-    "plugin_red_packet_nft_tip": "这是一个 NFT 红包。",
-    "plugin_red_packet_nft_no_history": "您还没有创建任何NFT红包。尝试创建红包并与您的朋友分享。",
-    "plugin_red_packet_attached_message": "附加信息",
-    "plugin_red_packet_from": "来自：@{{name}}",
-    "plugin_red_packet_description_claimed": "你获得了 {{amount}} {{symbol}}",
-    "plugin_red_packet_description_expired": "此红包已过期。",
-    "plugin_red_packet_description_refunded": "此红包已退款。",
-    "plugin_red_packet_description_refund": "您可以退回 {{balance}} {{symbol}}。",
-    "plugin_red_packet_description_empty": "此红包是空的。",
-    "plugin_red_packet_description_broken": "此红包已被损坏。",
-    "plugin_red_packet_description_failover": "{{shares}} 份额 / {{total}} {{symbol}} 总金额",
-    "plugin_red_packet_amount_per_share": "单个份额",
-    "plugin_red_packet_send_symbol": "发送 {{amount}} {{symbol}}",
-    "plugin_red_packet_amount_total": "总额",
-    "plugin_red_packet_next": "下一步",
-    "plugin_red_packet_back": "返回",
-    "plugin_red_packet_hint": "你可以在红包发送的24小时后提取红包余额。",
-    "plugin_red_packet_token": "代币",
-    "plugin_red_packet_message_label": "标题",
     "plugin_red_packet_create": "创建一个红包",
     "plugin_red_packet_create_with_token": "使用 {{symbol}} 创建红包",
-    "plugin_red_packet_history_duration": "时间: {{startTime}} ~ {{endTime}} (UTC+8)",
-    "plugin_red_packet_history_total_amount": "总额: {{amount}} {{symbol}}",
-    "plugin_red_packet_history_total_claimed_amount": "总额： <span><strong>{{claimedAmount}}/{{amount}}</strong></span> {{symbol}}",
-    "plugin_red_packet_history_claimed": "已认领： <strong>{{claimedShares}}/{{shares}}</strong> 份额",
-    "plugin_red_packet_history_split_mode": "分享模式：{{mode}}",
-    "plugin_red_packet_history_send": "发送",
     "plugin_nft_red_packet_create": "创建一个NFT红包",
     "plugin_red_packet_nft_account_name": "钱包账户",
-    "plugin_red_packet_nft_attached_message": "附加信息",
-    "plugin_red_packet_nft_total_amount": "总数",
-    "plugin_red_packet_nft_select_collection": "选择您的收藏品系列",
-    "plugin_red_packet_nft_max_shares": "NFT红包合约最多支持 {{amount}} 个NFT投放。",
-    "plugin_red_packet_nft_max_shares_tip": "NFT红包合约最多支持 {{amount}} 个NFT投放。",
     "plugin_red_packet_nft_shift_select_tip": "您也可以使用 <text>{{text}}</text> 选择多个NFT。",
-    "plugin_red_packet_nft_non_existed_tip": "Token ID <tokenIdList></tokenIdList> 不存在或不属于您。",
-    "plugin_red_packet_nft_select_all_option": "全部 ({{total}} NFT)",
-    "plugin_red_packet_nft_select_partially_option": "选择部分",
-    "plugin_red_packet_nft_approve_all_tip": "注意：在选择批准所有NFT时，合约中的所有NFT都将被授权默认销售，包括随后转移的NFT。",
-    "plugin_red_packet_completed": "已完成",
-    "plugin_red_packet_expired": "已过期",
-    "plugin_red_packet_indivisible": "每个份额的最小金额为 {{amount}} {{symbol}}",
-    "plugin_nft_red_packet_data_broken": "由于数据损坏，红包无法发送。",
     "plugin_gitcoin_readme": "通过使用此服务，您将同时向 <fund>Gitcoin grants development fund</fund> 捐赠您5%捐款。",
     "plugin_gitcoin_readme_fund_link": "https://gitcoin.co/gitcoin-sustainability-fund",
     "plugin_gitcoin_select_a_token": "选择代币",
diff --git a/packages/mask/shared-ui/locales/zh-TW.json b/packages/mask/shared-ui/locales/zh-TW.json
index 191164dbd49f..227d7a9a385e 100644
--- a/packages/mask/shared-ui/locales/zh-TW.json
+++ b/packages/mask/shared-ui/locales/zh-TW.json
@@ -78,7 +78,6 @@
     "reset": "重設",
     "editor": "編輯器",
     "retry": "重試",
-    "search": "搜尋",
     "go_wrong": "出錯了",
     "search_box_placeholder": "在這裡輸入以搜尋",
     "select_all": "選擇全部",
@@ -98,11 +97,7 @@
     "export": "導出",
     "wallet_name": "錢包名稱",
     "wallet_rename": "重新命名錢包",
-    "wallet_add_nft_invalid_owner": "此藏品不屬於你",
-    "wallet_add_nft_already_added": "此收藏品已被添加",
-    "wallet_loading_token": "讀取代幣中...",
     "wallet_loading_nft_contract": "加載NFT合約中...",
-    "wallet_search_no_result": "無結果",
     "wallet_confirm_with_password": "確認密碼",
     "wallet_airdrop_nft_unclaimed_title": "尚未認領的NFT空投",
     "plugin_external_loader_search_holder": "搜尋外部插件",
@@ -158,8 +153,6 @@
     "relative_time_years_ago": "{{years}} 年前",
     "plugin_wallet_guiding_step_1": "1. 選擇網路",
     "plugin_wallet_guiding_step_2": "2. 選擇錢包",
-    "plugin_wallet_collections": "收藏",
-    "plugin_wallet_select_a_token": "選擇一個代幣",
     "plugin_wallet_select_a_nft_contract": "選擇一份NFT合約",
     "plugin_wallet_select_a_nft_owner": "選擇一位NFT合約擁有者",
     "plugin_wallet_select_a_nft_operator": "選擇一位NFT合約執行者",
@@ -203,51 +196,7 @@
     "plugin_wallet_switch_network_under_going": "切換至 {{network}} 中",
     "plugin_wallet_not_available_on": "{{network}} 不可用",
     "plugin_wallet_connect_wallet": "連接錢包",
-    "plugin_red_packet_display_name": "插件：紅包",
-    "plugin_red_packet_claimed": "已認領",
-    "plugin_red_packet_erc20_tab_title": "代幣",
-    "plugin_red_packet_erc721_tab_title": "收藏品",
-    "plugin_red_packet_erc721_insufficient_balance": "餘額不足",
-    "plugin_red_packet_details": "紅包詳情",
-    "plugin_red_packet_split_mode": "分割模式",
-    "plugin_red_packet_average": "平均",
-    "plugin_red_packet_random": "隨機",
-    "plugin_red_packet_shares": "分享數",
-    "plugin_red_packet_best_wishes": "祝好運！",
-    "plugin_red_packet_create_new": "建立新的",
-    "plugin_red_packet_claim": "認領",
-    "plugin_red_packet_claiming": "認領中...",
-    "plugin_red_packet_refund": "退款",
-    "plugin_red_packet_empty": "空的",
-    "plugin_red_packet_refunding": "退款中",
-    "plugin_red_packet_select_existing": "選擇已存在的",
-    "plugin_red_packet_nft_tip": "這是一個NFT紅包",
-    "plugin_red_packet_attached_message": "附加的訊息",
-    "plugin_red_packet_from": "寄送人：{{name}}",
-    "plugin_red_packet_description_claimed": "你已經認領了 {{amount}} {{symbol}}。",
-    "plugin_red_packet_description_expired": "這個紅包已過期。",
-    "plugin_red_packet_description_refunded": "這個紅包已經被退款了。",
-    "plugin_red_packet_description_refund": "你可以退款 {{balance}} {{symbol}}。",
-    "plugin_red_packet_description_empty": "這個紅包是空的。",
-    "plugin_red_packet_description_broken": "這個紅包壞了",
-    "plugin_red_packet_description_failover": "寄送人 {{name}} / {{shares}} 分享數 / {{total}} {{symbol}}",
-    "plugin_red_packet_amount_per_share": "每次分享總額",
-    "plugin_red_packet_send_symbol": "送出 {{symbol}}",
-    "plugin_red_packet_amount_total": "總額",
-    "plugin_red_packet_next": "繼續",
-    "plugin_red_packet_back": "返回",
-    "plugin_red_packet_token": "代幣",
-    "plugin_red_packet_message_label": "標題",
     "plugin_red_packet_create_with_token": "使用 {{symbol}} 建立紅包",
-    "plugin_red_packet_history_duration": "時間: {{startTime}} ~ {{endTime}} (UTC+8)",
-    "plugin_red_packet_history_total_amount": "總額: {{amount}} {{symbol}}",
-    "plugin_red_packet_history_total_claimed_amount": "總共: <span><strong>{{claimedAmount}}/{{amount}}</strong></span> {{symbol}}",
-    "plugin_red_packet_history_claimed": "已認領: <strong>{{claimedShares}}/{{shares}}</strong> 分享數",
-    "plugin_red_packet_history_split_mode": "分割模式: {{mode}}",
-    "plugin_red_packet_history_send": "送出",
-    "plugin_red_packet_nft_total_amount": "總額",
-    "plugin_red_packet_completed": "已完成",
-    "plugin_red_packet_expired": "已過期",
     "plugin_gitcoin_readme": "通過使用此服務，您還將向 <fund>Gitcoin grants development fund</fund> 捐款 5%",
     "plugin_gitcoin_select_a_token": "選擇一個代幣",
     "plugin_gitcoin_enter_an_amount": "輸入一個金額",
diff --git a/packages/mask/src/components/CompositionDialog/useSubmit.ts b/packages/mask/src/components/CompositionDialog/useSubmit.ts
index 632718d60c6e..c9ac56b06735 100644
--- a/packages/mask/src/components/CompositionDialog/useSubmit.ts
+++ b/packages/mask/src/components/CompositionDialog/useSubmit.ts
@@ -1,15 +1,18 @@
+import { ImageTemplateTypes, socialNetworkEncoder } from '@masknet/encryption'
+import { FileInfoMetadataReader } from '@masknet/plugin-file-service'
+import type { ProfileIdentifier } from '@masknet/shared-base'
+import type { Meta } from '@masknet/typed-message'
 import { useCallback } from 'react'
 import Services from '../../extension/service'
+import { ITO_MetadataReader } from '../../plugins/ITO/SNSAdaptor/helpers'
 import { RedPacketMetadataReader } from '../../plugins/RedPacket/SNSAdaptor/helpers'
-import { ImageTemplateTypes, socialNetworkEncoder } from '@masknet/encryption'
 import { activatedSocialNetworkUI, globalUIState } from '../../social-network'
+import { isFacebook } from '../../social-network-adaptor/facebook.com/base'
 import { isTwitter } from '../../social-network-adaptor/twitter.com/base'
-import { useI18N } from '../../utils'
+import { I18NFunction, useI18N } from '../../utils'
+import { useLastRecognizedIdentity } from '../DataSource/useActivatedUI'
 import { SteganographyTextPayload } from '../InjectedComponents/SteganographyTextPayload'
 import type { SubmitComposition } from './CompositionUI'
-import { useLastRecognizedIdentity } from '../DataSource/useActivatedUI'
-import { isFacebook } from '../../social-network-adaptor/facebook.com/base'
-import type { ProfileIdentifier } from '@masknet/shared-base'
 
 export function useSubmit(onClose: () => void, reason: 'timeline' | 'popup' | 'reply') {
     const { t } = useI18N()
@@ -28,32 +31,19 @@ export function useSubmit(onClose: () => void, reason: 'timeline' | 'popup' | 'r
                 activatedSocialNetworkUI.encryptionNetwork,
             )
             const encrypted = socialNetworkEncoder(activatedSocialNetworkUI.encryptionNetwork, _encrypted)
+            const decoratedText = decorateEncryptedText(encrypted, t, content.meta)
 
-            const redPacketPreText =
-                isTwitter(activatedSocialNetworkUI) || isFacebook(activatedSocialNetworkUI)
-                    ? t('additional_post_box__encrypted_post_pre_red_packet_twitter_official_account', {
-                          encrypted,
-                          account: isTwitter(activatedSocialNetworkUI) ? t('twitter_account') : t('facebook_account'),
-                      })
-                    : t('additional_post_box__encrypted_post_pre_red_packet', { encrypted })
-
-            // TODO: move into the plugin system
-            const redPacketMetadata = RedPacketMetadataReader(content.meta)
             if (encode === 'image') {
                 const defaultText = t('additional_post_box__steganography_post_pre', {
                     random: new Date().toLocaleString(),
                 })
-                if (redPacketMetadata.ok) {
-                    await pasteImage(redPacketPreText.replace(encrypted, '') ?? defaultText, encrypted, 'eth', reason)
+                if (decoratedText !== null) {
+                    await pasteImage(decoratedText.replace(encrypted, ''), encrypted, 'eth', reason)
                 } else {
                     await pasteImage(defaultText, encrypted, 'v2', reason)
                 }
             } else {
-                pasteTextEncode(
-                    (redPacketMetadata.ok ? redPacketPreText : null) ??
-                        t('additional_post_box__encrypted_post_pre', { encrypted }),
-                    reason,
-                )
+                pasteTextEncode(decoratedText ?? t('additional_post_box__encrypted_post_pre', { encrypted }), reason)
             }
             onClose()
         },
@@ -81,3 +71,34 @@ async function pasteImage(
         reason,
     })
 }
+
+// TODO: Provide API to plugin to post-process post content,
+// then we can move these -PreText's and meta readers into plugin's own context
+function decorateEncryptedText(encrypted: string, t: I18NFunction, meta?: Meta) {
+    const hasOfficialAccount = isTwitter(activatedSocialNetworkUI) || isFacebook(activatedSocialNetworkUI)
+    const officialAccount = isTwitter(activatedSocialNetworkUI) ? t('twitter_account') : t('facebook_account')
+
+    if (RedPacketMetadataReader(meta).ok) {
+        return hasOfficialAccount
+            ? t('additional_post_box__encrypted_post_pre_red_packet_twitter_official_account', {
+                  encrypted,
+                  account: officialAccount,
+              })
+            : t('additional_post_box__encrypted_post_pre_red_packet', { encrypted })
+    } else if (ITO_MetadataReader(meta).ok) {
+        return hasOfficialAccount
+            ? t('additional_post_box__encrypted_post_pre_ito_twitter_official_account', {
+                  encrypted,
+                  account: officialAccount,
+              })
+            : t('additional_post_box__encrypted_post_pre_ito', { encrypted })
+    } else if (FileInfoMetadataReader(meta).ok) {
+        return hasOfficialAccount
+            ? t('additional_post_box__encrypted_post_pre_file_service_twitter_official_account', {
+                  encrypted,
+                  account: officialAccount,
+              })
+            : t('additional_post_box__encrypted_post_pre_file_service', { encrypted })
+    }
+    return null
+}
diff --git a/packages/mask/src/plugins/Avatar/SNSAdaptor/NFTAvatar.tsx b/packages/mask/src/plugins/Avatar/SNSAdaptor/NFTAvatar.tsx
index c8fa3e40e34a..c9b9a5b13d30 100644
--- a/packages/mask/src/plugins/Avatar/SNSAdaptor/NFTAvatar.tsx
+++ b/packages/mask/src/plugins/Avatar/SNSAdaptor/NFTAvatar.tsx
@@ -16,7 +16,7 @@ import { useI18N } from '../../../utils'
 import { EthereumChainBoundary } from '../../../web3/UI/EthereumChainBoundary'
 import { AddNFT } from './AddNFT'
 import { NFTImage } from './NFTImage'
-import { useAccount, useWeb3State } from '@masknet/plugin-infra/web3'
+import { useAccount } from '@masknet/plugin-infra/web3'
 import { ReversedAddress } from '@masknet/shared'
 
 const useStyles = makeStyles()((theme) => ({
@@ -101,7 +101,6 @@ export function NFTAvatar(props: NFTAvatarProps) {
     const [open_, setOpen_] = useState(false)
     const [collectibles_, setCollectibles_] = useState<ERC721TokenDetailed[]>([])
     const { t } = useI18N()
-    const { Utils } = useWeb3State()
     const { data: collectibles, error, retry, state } = useCollectibles(account, ChainId.Mainnet)
 
     const onClick = useCallback(async () => {
diff --git a/packages/mask/src/plugins/Collectible/SNSAdaptor/MakeOfferDialog.tsx b/packages/mask/src/plugins/Collectible/SNSAdaptor/MakeOfferDialog.tsx
index 3fa81e9a5a0e..6dd545540db2 100644
--- a/packages/mask/src/plugins/Collectible/SNSAdaptor/MakeOfferDialog.tsx
+++ b/packages/mask/src/plugins/Collectible/SNSAdaptor/MakeOfferDialog.tsx
@@ -38,6 +38,7 @@ import type { Coin } from '../../Trader/types'
 import { SelectTokenListPanel } from './SelectTokenListPanel'
 import { isWyvernSchemaName } from '../utils'
 import { EthereumChainBoundary } from '../../../web3/UI/EthereumChainBoundary'
+import { activatedSocialNetworkUI } from '../../../social-network'
 
 const useStyles = makeStyles()((theme) => {
     return {
@@ -118,7 +119,8 @@ export function MakeOfferDialog(props: MakeOfferDialogProps) {
             expirationTime: !isAuction ? getUnixTime(expirationDateTime) : undefined,
             paymentTokenAddress: token.value.type === EthereumTokenType.Native ? undefined : token.value.address,
         })
-    }, [asset?.value, token, account, amount, expirationDateTime, isAuction])
+        activatedSocialNetworkUI.utils.share?.(t('promote_collectible'))
+    }, [asset?.value, token, account, amount, expirationDateTime, isAuction, t])
 
     const { setDialog: openSwapDialog } = useRemoteControlledDialog(PluginTraderMessages.swapDialogUpdated)
 
diff --git a/packages/mask/src/plugins/Gitcoin/SNSAdaptor/DonateDialog.tsx b/packages/mask/src/plugins/Gitcoin/SNSAdaptor/DonateDialog.tsx
index eb98758360ab..ee480fa8ef58 100644
--- a/packages/mask/src/plugins/Gitcoin/SNSAdaptor/DonateDialog.tsx
+++ b/packages/mask/src/plugins/Gitcoin/SNSAdaptor/DonateDialog.tsx
@@ -14,16 +14,16 @@ import {
 } from '@masknet/web3-shared-evm'
 import { DialogContent, Link, Typography } from '@mui/material'
 import { useCallback, useMemo, useState } from 'react'
-import { Trans } from 'react-i18next'
 import ActionButton from '../../../extension/options-page/DashboardComponents/ActionButton'
 import { activatedSocialNetworkUI } from '../../../social-network'
 import { isFacebook } from '../../../social-network-adaptor/facebook.com/base'
 import { isTwitter } from '../../../social-network-adaptor/twitter.com/base'
-import { useI18N } from '../../../utils'
+import { useI18N as useBaseI18N } from '../../../utils'
 import { EthereumERC20TokenApprovedBoundary } from '../../../web3/UI/EthereumERC20TokenApprovedBoundary'
 import { EthereumWalletConnectedBoundary } from '../../../web3/UI/EthereumWalletConnectedBoundary'
 import { TokenAmountPanel } from '../../../web3/UI/TokenAmountPanel'
 import { useDonateCallback } from '../hooks/useDonateCallback'
+import { Translate, useI18N } from '../locales'
 import { PluginGitcoinMessages } from '../messages'
 
 const useStyles = makeStyles()((theme) => ({
@@ -52,7 +52,8 @@ const useStyles = makeStyles()((theme) => ({
 export interface DonateDialogProps extends withClasses<never> {}
 
 export function DonateDialog(props: DonateDialogProps) {
-    const { t } = useI18N()
+    const { t: tr } = useBaseI18N()
+    const t = useI18N()
     const classes = useStylesExtends(useStyles(), props)
     const [title, setTitle] = useState('')
     const [address, setAddress] = useState('')
@@ -112,10 +113,11 @@ export function DonateDialog(props: DonateDialogProps) {
                   `I just donated ${title} with ${formatBalance(amount, token.decimals)} ${cashTag}${token.symbol}. ${
                       isTwitter(activatedSocialNetworkUI) || isFacebook(activatedSocialNetworkUI)
                           ? `Follow @${
-                                isTwitter(activatedSocialNetworkUI) ? t('twitter_account') : t('facebook_account')
+                                isTwitter(activatedSocialNetworkUI) ? tr('twitter_account') : tr('facebook_account')
                             } (mask.io) to donate Gitcoin grants.`
                           : ''
                   }`,
+                  t.promote(),
                   '#mask_io',
                   postLink,
               ].join('\n')
@@ -126,17 +128,17 @@ export function DonateDialog(props: DonateDialogProps) {
                 activatedSocialNetworkUI.utils.share?.(shareText)
             },
         })
-    }, [openShareTxDialog, token, donateCallback])
+    }, [openShareTxDialog, token, donateCallback, tr, t])
 
     // #region submit button
     const validationMessage = useMemo(() => {
-        if (!token) return t('plugin_gitcoin_select_a_token')
-        if (!account) return t('plugin_wallet_connect_a_wallet')
-        if (!address) return t('plugin_gitcoin_grant_not_available')
-        if (!amount || amount.isZero()) return t('plugin_gitcoin_enter_an_amount')
+        if (!token) return t.select_a_token()
+        if (!account) return tr('plugin_wallet_connect_a_wallet')
+        if (!address) return t.grant_not_available()
+        if (!amount || amount.isZero()) return t.enter_an_amount()
         if (amount.isGreaterThan(tokenBalance.value ?? '0'))
-            return t('plugin_gitcoin_insufficient_balance', {
-                symbol: token.symbol,
+            return t.insufficient_balance({
+                symbol: token.symbol!,
             })
         return ''
     }, [account, address, amount.toFixed(), chainId, token, tokenBalance.value ?? '0'])
@@ -164,16 +166,9 @@ export function DonateDialog(props: DonateDialogProps) {
                         />
                     </form>
                     <Typography className={classes.tip} variant="body1">
-                        <Trans
-                            i18nKey="plugin_gitcoin_readme"
+                        <Translate.gitcoin_readme
                             components={{
-                                fund: (
-                                    <Link
-                                        target="_blank"
-                                        rel="noopener noreferrer"
-                                        href={t('plugin_gitcoin_readme_fund_link')}
-                                    />
-                                ),
+                                fund: <Link target="_blank" rel="noopener noreferrer" href={t.readme_fund_link()} />,
                             }}
                         />
                     </Typography>
@@ -190,7 +185,7 @@ export function DonateDialog(props: DonateDialogProps) {
                                 disabled={!!validationMessage || loading}
                                 onClick={donate}
                                 variant="contained">
-                                {validationMessage || t('plugin_gitcoin_donate')}
+                                {validationMessage || t.donate()}
                             </ActionButton>
                         </EthereumERC20TokenApprovedBoundary>
                     </EthereumWalletConnectedBoundary>
diff --git a/packages/mask/src/plugins/Gitcoin/SNSAdaptor/PreviewCard.tsx b/packages/mask/src/plugins/Gitcoin/SNSAdaptor/PreviewCard.tsx
index 154c34876ab4..142ab0b521c5 100644
--- a/packages/mask/src/plugins/Gitcoin/SNSAdaptor/PreviewCard.tsx
+++ b/packages/mask/src/plugins/Gitcoin/SNSAdaptor/PreviewCard.tsx
@@ -3,7 +3,8 @@ import { Box, Card, Typography, Button, Avatar, CircularProgress, useTheme } fro
 import { makeStyles } from '@masknet/theme'
 import QueryBuilderIcon from '@mui/icons-material/QueryBuilder'
 import VerifiedUserIcon from '@mui/icons-material/VerifiedUser'
-import { useI18N } from '../../../utils'
+import { useI18N as useBaseI18N } from '../../../utils'
+import { useI18N } from '../locales'
 import { useRemoteControlledDialog } from '@masknet/shared-base-ui'
 import { useGrant } from '../hooks/useGrant'
 import { PluginGitcoinMessages } from '../messages'
@@ -87,7 +88,8 @@ interface PreviewCardProps {
 }
 
 export function PreviewCard(props: PreviewCardProps) {
-    const { t } = useI18N()
+    const { t: tr } = useBaseI18N()
+    const t = useI18N()
     const { classes } = useStyles()
     const { value: grant, error, loading, retry } = useGrant(props.id)
     const chainId = useChainId(NetworkPluginID.PLUGIN_EVM)
@@ -117,7 +119,7 @@ export function PreviewCard(props: PreviewCardProps) {
     if (error)
         return (
             <Box display="flex" flexDirection="column" alignItems="center" sx={{ padding: 1.5 }}>
-                <Typography color="textPrimary">{t('go_wrong')}</Typography>
+                <Typography color="textPrimary">{tr('go_wrong')}</Typography>
                 <Button
                     sx={{
                         backgroundColor: theme.palette.maskColor.dark,
@@ -128,7 +130,7 @@ export function PreviewCard(props: PreviewCardProps) {
                         color: 'white',
                     }}
                     onClick={retry}>
-                    {t('retry')}
+                    {tr('retry')}
                 </Button>
             </Box>
         )
@@ -155,12 +157,12 @@ export function PreviewCard(props: PreviewCardProps) {
                     <div className={classes.meta}>
                         <QueryBuilderIcon fontSize="small" color="disabled" />
                         <Typography variant="body2" color="textSecondary">
-                            {t('plugin_gitcoin_last_updated')} {grant.last_update_natural}
+                            {t.last_updated()} {grant.last_update_natural}
                         </Typography>
                     </div>
                     <div className={classes.meta}>
                         <Typography variant="body2" color="textSecondary">
-                            {t('plugin_gitcoin_by')}
+                            {t.by()}
                         </Typography>
                         <Avatar
                             alt={grant.admin_profile.handle}
@@ -181,7 +183,7 @@ export function PreviewCard(props: PreviewCardProps) {
                         target="_blank"
                         rel="noopener noreferrer"
                         href={urlcat('https://gitcoin.co', grant.url)}>
-                        {t('plugin_gitcoin_view_on')}
+                        {t.view_on()}
                     </Button>
                 </Box>
                 <Box sx={{ flex: 1, padding: 1.5 }}>
@@ -199,7 +201,7 @@ export function PreviewCard(props: PreviewCardProps) {
                                 color: 'white',
                             }}
                             onClick={onDonate}>
-                            {t('plugin_gitcoin_donate')}
+                            {t.donate()}
                         </Button>
                     </EthereumChainBoundary>
                 </Box>
diff --git a/packages/mask/src/plugins/Gitcoin/SNSAdaptor/index.tsx b/packages/mask/src/plugins/Gitcoin/SNSAdaptor/index.tsx
index 956f016b34d3..1ffb8a4109eb 100644
--- a/packages/mask/src/plugins/Gitcoin/SNSAdaptor/index.tsx
+++ b/packages/mask/src/plugins/Gitcoin/SNSAdaptor/index.tsx
@@ -1,18 +1,15 @@
-import { useMemo } from 'react'
-import { ChainId } from '@masknet/web3-shared-evm'
-import { usePostInfoDetails, Plugin, usePluginWrapper } from '@masknet/plugin-infra/content-script'
-import { NetworkPluginID, useChainId } from '@masknet/plugin-infra/web3'
-import { extractTextFromTypedMessage } from '@masknet/typed-message'
-import { parseURL } from '@masknet/shared-base'
 import { GitcoinIcon } from '@masknet/icons'
-import { PreviewCard } from './PreviewCard'
+import { Plugin, PluginId, usePluginWrapper, usePostInfoDetails } from '@masknet/plugin-infra/content-script'
+import { parseURL } from '@masknet/shared-base'
+import { extractTextFromTypedMessage } from '@masknet/typed-message'
+import { useMemo } from 'react'
 import { Trans } from 'react-i18next'
 import { base } from '../base'
-import { PLUGIN_NAME, PLUGIN_META_KEY } from '../constants'
+import { PLUGIN_META_KEY, PLUGIN_NAME } from '../constants'
 import { DonateDialog } from './DonateDialog'
+import { PreviewCard } from './PreviewCard'
 
 const isGitcoin = (x: string): boolean => /^https:\/\/gitcoin.co\/grants\/\d+/.test(x)
-const isGitCoinSupported = (chainId: ChainId) => [ChainId.Mainnet, ChainId.Matic].includes(chainId)
 
 const sns: Plugin.SNSAdaptor.Definition = {
     ...base,
@@ -41,8 +38,8 @@ const sns: Plugin.SNSAdaptor.Definition = {
         {
             ApplicationEntryID: base.ID,
             category: 'dapp',
-            description: <Trans i18nKey="plugin_gitcoin_description" />,
-            name: <Trans i18nKey="plugin_gitcoin_name" />,
+            description: <Trans ns={PluginId.Gitcoin} i18nKey="description" />,
+            name: <Trans ns={PluginId.Gitcoin} i18nKey="name" />,
             icon: <GitcoinIcon />,
             marketListSortingPriority: 9,
             tutorialLink: 'https://realmasknetwork.notion.site/98ed83784ed4446a8a13fa685c7bddfb',
@@ -52,7 +49,6 @@ const sns: Plugin.SNSAdaptor.Definition = {
 
 function Renderer(props: React.PropsWithChildren<{ url: string }>) {
     const [id = ''] = props.url.match(/\d+/) ?? []
-    const chainId = useChainId(NetworkPluginID.PLUGIN_EVM)
     usePluginWrapper(true)
     return <PreviewCard id={id} />
 }
diff --git a/packages/mask/src/plugins/Gitcoin/base.ts b/packages/mask/src/plugins/Gitcoin/base.ts
index 2ba903193d15..399adeb050cb 100644
--- a/packages/mask/src/plugins/Gitcoin/base.ts
+++ b/packages/mask/src/plugins/Gitcoin/base.ts
@@ -2,6 +2,7 @@ import type { Plugin } from '@masknet/plugin-infra'
 import { NetworkPluginID } from '@masknet/plugin-infra/web3'
 import { ChainId } from '@masknet/web3-shared-evm'
 import { PLUGIN_ID, PLUGIN_NAME, PLUGIN_DESCRIPTION } from './constants'
+import { languages } from './locales/languages'
 
 export const base: Plugin.Shared.Definition = {
     ID: PLUGIN_ID,
@@ -21,4 +22,5 @@ export const base: Plugin.Shared.Definition = {
         },
     },
     contribution: { postContent: new Set([/https:\/\/gitcoin.co\/grants\/\d+/]) },
+    i18n: languages,
 }
diff --git a/packages/mask/src/plugins/Gitcoin/locales/en-US.json b/packages/mask/src/plugins/Gitcoin/locales/en-US.json
new file mode 100644
index 000000000000..96bdf7b52625
--- /dev/null
+++ b/packages/mask/src/plugins/Gitcoin/locales/en-US.json
@@ -0,0 +1,15 @@
+{
+    "name": "Gitcoin",
+    "description": "Display specific information of Gitcoin projects, donate to a project directly on social media.",
+    "promote": "Share or browse Gitcoin projects you have donated or interested in.",
+    "select_a_token": "Select a token",
+    "grant_not_available": "Grant not available",
+    "enter_an_amount": "Enter an amount",
+    "insufficient_balance": "Insufficient {{symbol}} balance",
+    "gitcoin_readme": "By using this service, you will also be contributing 5% of your contribution to the <fund>Gitcoin grants development fund</fund>.",
+    "donate": "Donate",
+    "last_updated": "Last update:",
+    "by": "By",
+    "view_on": "View on Gitcoin",
+    "readme_fund_link": "https://gitcoin.co/grants/86/gitcoin-sustainability-fund"
+}
diff --git a/packages/mask/src/plugins/Gitcoin/locales/index.ts b/packages/mask/src/plugins/Gitcoin/locales/index.ts
new file mode 100644
index 000000000000..d6ead60252e4
--- /dev/null
+++ b/packages/mask/src/plugins/Gitcoin/locales/index.ts
@@ -0,0 +1,6 @@
+// This file is auto generated. DO NOT EDIT
+// Run `npx gulp sync-languages` to regenerate.
+// Default fallback language in a family of languages are chosen by the alphabet order
+// To overwrite this, please overwrite packages/scripts/src/locale-kit-next/index.ts
+
+export * from './i18n_generated'
diff --git a/packages/mask/src/plugins/Gitcoin/locales/ja-JP.json b/packages/mask/src/plugins/Gitcoin/locales/ja-JP.json
new file mode 100644
index 000000000000..0967ef424bce
--- /dev/null
+++ b/packages/mask/src/plugins/Gitcoin/locales/ja-JP.json
@@ -0,0 +1 @@
+{}
diff --git a/packages/mask/src/plugins/Gitcoin/locales/ko-KR.json b/packages/mask/src/plugins/Gitcoin/locales/ko-KR.json
new file mode 100644
index 000000000000..0967ef424bce
--- /dev/null
+++ b/packages/mask/src/plugins/Gitcoin/locales/ko-KR.json
@@ -0,0 +1 @@
+{}
diff --git a/packages/mask/src/plugins/Gitcoin/locales/languages.ts b/packages/mask/src/plugins/Gitcoin/locales/languages.ts
new file mode 100644
index 000000000000..9b496c047c8a
--- /dev/null
+++ b/packages/mask/src/plugins/Gitcoin/locales/languages.ts
@@ -0,0 +1,34 @@
+// This file is auto generated. DO NOT EDIT
+// Run `npx gulp sync-languages` to regenerate.
+// Default fallback language in a family of languages are chosen by the alphabet order
+// To overwrite this, please overwrite packages/scripts/src/locale-kit-next/index.ts
+import en_US from './en-US.json'
+import ja_JP from './ja-JP.json'
+import ko_KR from './ko-KR.json'
+import qya_AA from './qya-AA.json'
+import zh_CN from './zh-CN.json'
+import zh_TW from './zh-TW.json'
+export const languages = {
+    en: en_US,
+    ja: ja_JP,
+    ko: ko_KR,
+    qy: qya_AA,
+    'zh-CN': zh_CN,
+    zh: zh_TW,
+}
+// @ts-ignore
+if (import.meta.webpackHot) {
+    // @ts-ignore
+    import.meta.webpackHot.accept(
+        ['./en-US.json', './ja-JP.json', './ko-KR.json', './qya-AA.json', './zh-CN.json', './zh-TW.json'],
+        () =>
+            globalThis.dispatchEvent?.(
+                new CustomEvent('MASK_I18N_HMR', {
+                    detail: [
+                        'com.maskbook.gitcoin',
+                        { en: en_US, ja: ja_JP, ko: ko_KR, qy: qya_AA, 'zh-CN': zh_CN, zh: zh_TW },
+                    ],
+                }),
+            ),
+    )
+}
diff --git a/packages/mask/src/plugins/Gitcoin/locales/qya-AA.json b/packages/mask/src/plugins/Gitcoin/locales/qya-AA.json
new file mode 100644
index 000000000000..0967ef424bce
--- /dev/null
+++ b/packages/mask/src/plugins/Gitcoin/locales/qya-AA.json
@@ -0,0 +1 @@
+{}
diff --git a/packages/mask/src/plugins/Gitcoin/locales/zh-CN.json b/packages/mask/src/plugins/Gitcoin/locales/zh-CN.json
new file mode 100644
index 000000000000..0967ef424bce
--- /dev/null
+++ b/packages/mask/src/plugins/Gitcoin/locales/zh-CN.json
@@ -0,0 +1 @@
+{}
diff --git a/packages/mask/src/plugins/Gitcoin/locales/zh-TW.json b/packages/mask/src/plugins/Gitcoin/locales/zh-TW.json
new file mode 100644
index 000000000000..0967ef424bce
--- /dev/null
+++ b/packages/mask/src/plugins/Gitcoin/locales/zh-TW.json
@@ -0,0 +1 @@
+{}
diff --git a/packages/mask/src/plugins/ITO/SNSAdaptor/ITO.tsx b/packages/mask/src/plugins/ITO/SNSAdaptor/ITO.tsx
index 7b761a3bb0fb..325506b44221 100644
--- a/packages/mask/src/plugins/ITO/SNSAdaptor/ITO.tsx
+++ b/packages/mask/src/plugins/ITO/SNSAdaptor/ITO.tsx
@@ -5,12 +5,9 @@ import {
     FungibleTokenDetailed,
     getChainDetailed,
     isSameAddress,
-    currySameAddress,
     resolveLinkOnExplorer,
-    TransactionStateType,
     useAccount,
     useChainId,
-    useChainIdValid,
     useTokenConstants,
     isNativeTokenAddress,
 } from '@masknet/web3-shared-evm'
@@ -246,8 +243,7 @@ export function ITO(props: ITO_Props) {
     const account = useAccount()
     const postLink = usePostLink()
     const chainId = useChainId()
-    const chainIdValid = useChainIdValid()
-    const [destructState, destructCallback, resetDestructCallback] = useDestructCallback(props.payload.contract_address)
+    const [, destructCallback] = useDestructCallback(props.payload.contract_address)
     const [openClaimDialog, setOpenClaimDialog] = useState(false)
     const [claimDialogStatus, setClaimDialogStatus] = useState(SwapStatus.Remind)
 
@@ -414,23 +410,8 @@ export function ITO(props: ITO_Props) {
         return () => clearTimeout(timer)
     }, [endTime, listOfStatus])
 
-    useEffect(() => {
-        if (destructState.type === TransactionStateType.UNKNOWN || !canWithdraw) return
-        let summary = t('plugin_ito_withdraw')
-        if (!noRemain) {
-            summary += ' ' + formatBalance(total_remaining, token.decimals) + ' ' + token.symbol
-        }
-        availability?.exchange_addrs.forEach((addr, i) => {
-            const token = exchange_tokens.find(currySameAddress(addr))
-            const comma = noRemain && i === 0 ? ' ' : ', '
-            if (token) {
-                summary += comma + formatBalance(availability?.exchanged_tokens[i], token.decimals) + ' ' + token.symbol
-            }
-        })
-    }, [destructState, canWithdraw])
-
     const onWithdraw = useCallback(async () => {
-        destructCallback(payload.pid)
+        await destructCallback(payload.pid)
     }, [destructCallback, payload.pid])
     // #endregion
 
diff --git a/packages/mask/src/plugins/ITO/SNSAdaptor/NftAirdropCard.tsx b/packages/mask/src/plugins/ITO/SNSAdaptor/NftAirdropCard.tsx
index 7190867404a9..f4eaba6d32be 100644
--- a/packages/mask/src/plugins/ITO/SNSAdaptor/NftAirdropCard.tsx
+++ b/packages/mask/src/plugins/ITO/SNSAdaptor/NftAirdropCard.tsx
@@ -1,17 +1,11 @@
 import formatDateTime from 'date-fns/format'
-import {
-    ChainId,
-    TransactionStateType,
-    useAccount,
-    resolveTransactionLinkOnExplorer,
-    useChainId,
-} from '@masknet/web3-shared-evm'
+import { ChainId, useAccount, resolveTransactionLinkOnExplorer, useChainId } from '@masknet/web3-shared-evm'
 import { EthereumWalletConnectedBoundary } from '../../../web3/UI/EthereumWalletConnectedBoundary'
 import { Box, Typography, Button, TextField, CircularProgress, Link } from '@mui/material'
 import { useSpaceStationClaimableTokenCountCallback } from './hooks/useSpaceStationClaimableTokenCountCallback'
 import { useSpaceStationContractClaimCallback } from './hooks/useSpaceStationContractClaimCallback'
 import { useI18N } from '../../../utils'
-import { useState, useEffect } from 'react'
+import { useState, useEffect, useCallback } from 'react'
 import { makeStyles, useCustomSnackbar, OptionsObject } from '@masknet/theme'
 import OpenInNewIcon from '@mui/icons-material/OpenInNew'
 import CloseIcon from '@mui/icons-material/Close'
@@ -287,8 +281,9 @@ function ClaimItem(props: ClaimItemProps) {
             horizontal: 'center',
         },
     }
-    useEffect(() => {
-        if (claimState.type === TransactionStateType.CONFIRMED && claimState.no === 0) {
+    const claim = useCallback(async () => {
+        const hash = await claimCallback()
+        if (typeof hash === 'string') {
             showSnackbar(
                 <div className={classes.snackbarContent}>
                     <div className={classes.snackbarTipContent}>
@@ -299,10 +294,7 @@ function ClaimItem(props: ClaimItemProps) {
                                 className={classes.whiteText}
                                 target="_blank"
                                 rel="noopener noreferrer"
-                                href={resolveTransactionLinkOnExplorer(
-                                    ChainId.Mumbai,
-                                    claimState.receipt.transactionHash,
-                                )}>
+                                href={resolveTransactionLinkOnExplorer(ChainId.Mumbai, hash)}>
                                 <OpenInNewIcon className={classes.snackbarIcon} />
                             </Link>
                         </Typography>
@@ -316,7 +308,7 @@ function ClaimItem(props: ClaimItemProps) {
             )
 
             retry()
-        } else if (claimState.type === TransactionStateType.FAILED) {
+        } else {
             showSnackbar(
                 <div className={classes.snackbarContent}>
                     <div className={classes.snackbarTipContent}>
@@ -331,7 +323,7 @@ function ClaimItem(props: ClaimItemProps) {
                 } as OptionsObject,
             )
         }
-    }, [claimState])
+    }, [showSnackbar])
 
     const unClaimable =
         now < campaignInfo.startTime * 1000 ||
@@ -359,17 +351,10 @@ function ClaimItem(props: ClaimItemProps) {
                             unlockMetaMask: classNames(classes.actionButton, classes.connectWallet),
                         }}>
                         <ActionButton
-                            disabled={
-                                claimState.type === TransactionStateType.WAIT_FOR_CONFIRMING ||
-                                claimState.type === TransactionStateType.HASH ||
-                                unClaimable
-                            }
-                            loading={
-                                claimState.type === TransactionStateType.WAIT_FOR_CONFIRMING ||
-                                claimState.type === TransactionStateType.HASH
-                            }
+                            disabled={claimState.loading || unClaimable}
+                            loading={claimState.loading || unClaimable}
                             classes={{ disabled: classes.disabledButton }}
-                            onClick={claimCallback}
+                            onClick={claim}
                             className={classes.actionButton}>
                             <span>
                                 {claimed
diff --git a/packages/mask/src/plugins/ITO/SNSAdaptor/PoolInList.tsx b/packages/mask/src/plugins/ITO/SNSAdaptor/PoolInList.tsx
index e044f04798a5..67441435125f 100644
--- a/packages/mask/src/plugins/ITO/SNSAdaptor/PoolInList.tsx
+++ b/packages/mask/src/plugins/ITO/SNSAdaptor/PoolInList.tsx
@@ -5,7 +5,6 @@ import {
     FungibleTokenInitial,
     getChainDetailed,
     isSameAddress,
-    TransactionStateType,
     useAccount,
     useFungibleTokenDetailed,
     useFungibleTokensDetailed,
@@ -35,10 +34,10 @@ import { useAvailabilityComputed } from './hooks/useAvailabilityComputed'
 import { usePoolTradeInfo } from './hooks/usePoolTradeInfo'
 import { ITO_Status, JSON_PayloadFromChain, JSON_PayloadInMask, PoolFromNetwork } from '../types'
 import { useDestructCallback } from './hooks/useDestructCallback'
-import { useTransactionDialog } from '../../../web3/hooks/useTransactionDialog'
 import { omit } from 'lodash-unified'
 import { useSubscription } from 'use-subscription'
 import { PersistentStorages } from '../../../../shared'
+import { useCallback } from 'react'
 
 const useStyles = makeStyles()((theme) => {
     const smallQuery = `@media (max-width: ${theme.breakpoints.values.sm}px)`
@@ -175,11 +174,14 @@ export function PoolInList(props: PoolInListProps) {
     // #endregion
 
     // #region withdraw
-    const [destructState, destructCallback, resetDestructCallback] = useDestructCallback(pool.contract_address)
-    useTransactionDialog(null, destructState, TransactionStateType.CONFIRMED, () => {
-        onRetry()
-        resetDestructCallback()
-    })
+    const [{ loading: destructing }, destructCallback] = useDestructCallback(pool.contract_address)
+    const destruct = useCallback(
+        async (pid: string) => {
+            await destructCallback(pid)
+            onRetry()
+        },
+        [destructCallback, onRetry],
+    )
     // #endregion
 
     const account = useAccount()
@@ -202,7 +204,13 @@ export function PoolInList(props: PoolInListProps) {
         return (
             <>
                 {loadingTradeInfo || loadingAvailability ? null : canWithdraw ? (
-                    <ActionButton fullWidth size="small" variant="contained" onClick={() => destructCallback(pool.pid)}>
+                    <ActionButton
+                        loading={destructing}
+                        disabled={destructing}
+                        fullWidth
+                        size="small"
+                        variant="contained"
+                        onClick={() => destruct(pool.pid)}>
                         {t('plugin_ito_withdraw')}
                     </ActionButton>
                 ) : canSend ? (
diff --git a/packages/mask/src/plugins/ITO/SNSAdaptor/hooks/useDestructCallback.ts b/packages/mask/src/plugins/ITO/SNSAdaptor/hooks/useDestructCallback.ts
index 2e736befd4e6..2176cfce5699 100644
--- a/packages/mask/src/plugins/ITO/SNSAdaptor/hooks/useDestructCallback.ts
+++ b/packages/mask/src/plugins/ITO/SNSAdaptor/hooks/useDestructCallback.ts
@@ -1,43 +1,22 @@
-import { useCallback } from 'react'
-import type { TransactionReceipt } from 'web3-core'
 import type { NonPayableTx } from '@masknet/web3-contracts/types/types'
-import { TransactionEventType, TransactionStateType, useAccount, useTransactionState } from '@masknet/web3-shared-evm'
+import { TransactionEventType, useAccount } from '@masknet/web3-shared-evm'
+import { useAsyncFn } from 'react-use'
 import { useITO_Contract } from './useITO_Contract'
 
 export function useDestructCallback(ito_address: string) {
     const account = useAccount()
     const { contract: ITO_Contract } = useITO_Contract(ito_address)
-    const [destructState, setDestructState] = useTransactionState()
 
-    const destructCallback = useCallback(
+    return useAsyncFn(
         async (id: string) => {
-            if (!ITO_Contract || !id) {
-                setDestructState({
-                    type: TransactionStateType.UNKNOWN,
-                })
-                return
-            }
-
-            // start waiting for provider to confirm tx
-            setDestructState({
-                type: TransactionStateType.WAIT_FOR_CONFIRMING,
-            })
+            if (!ITO_Contract || !id) return
 
             // estimate gas and compose transaction
             const config = {
                 from: account,
-                gas: await ITO_Contract.methods
-                    .destruct(id)
-                    .estimateGas({
-                        from: account,
-                    })
-                    .catch((error: Error) => {
-                        setDestructState({
-                            type: TransactionStateType.FAILED,
-                            error,
-                        })
-                        throw error
-                    }),
+                gas: await ITO_Contract.methods.destruct(id).estimateGas({
+                    from: account,
+                }),
             }
 
             // send transaction and wait for hash
@@ -45,39 +24,14 @@ export function useDestructCallback(ito_address: string) {
                 ITO_Contract.methods
                     .destruct(id)
                     .send(config as NonPayableTx)
-                    .on(TransactionEventType.RECEIPT, (receipt: TransactionReceipt) => {
-                        setDestructState({
-                            type: TransactionStateType.CONFIRMED,
-                            no: 0,
-                            receipt,
-                        })
-                        resolve(receipt.transactionHash)
-                    })
-                    .on(TransactionEventType.CONFIRMATION, (no: number, receipt: TransactionReceipt) => {
-                        setDestructState({
-                            type: TransactionStateType.CONFIRMED,
-                            no,
-                            receipt,
-                        })
+                    .on(TransactionEventType.CONFIRMATION, (no, receipt) => {
                         resolve(receipt.transactionHash)
                     })
                     .on(TransactionEventType.ERROR, (error: Error) => {
-                        setDestructState({
-                            type: TransactionStateType.FAILED,
-                            error,
-                        })
                         reject(error)
                     })
             })
         },
         [ITO_Contract],
     )
-
-    const resetCallback = useCallback(() => {
-        setDestructState({
-            type: TransactionStateType.UNKNOWN,
-        })
-    }, [])
-
-    return [destructState, destructCallback, resetCallback] as const
 }
diff --git a/packages/mask/src/plugins/ITO/SNSAdaptor/hooks/useSpaceStationContractClaimCallback.ts b/packages/mask/src/plugins/ITO/SNSAdaptor/hooks/useSpaceStationContractClaimCallback.ts
index 3ceca469f049..ab72cf70ea35 100644
--- a/packages/mask/src/plugins/ITO/SNSAdaptor/hooks/useSpaceStationContractClaimCallback.ts
+++ b/packages/mask/src/plugins/ITO/SNSAdaptor/hooks/useSpaceStationContractClaimCallback.ts
@@ -1,49 +1,28 @@
-import { useSpaceStationContract } from './useSpaceStationContract'
-import {
-    useAccount,
-    useSpaceStationGalaxyConstants,
-    useTransactionState,
-    TransactionStateType,
-    TransactionEventType,
-} from '@masknet/web3-shared-evm'
-import { useCallback } from 'react'
-import type { CampaignInfo } from '../../types'
 import type { SpaceStationGalaxy } from '@masknet/web3-contracts/types/SpaceStationGalaxy'
 import type { NonPayableTx } from '@masknet/web3-contracts/types/types'
-import { getAccountClaimSignature, mutationParticipate } from '../../Worker/apis/spaceStationGalaxy'
+import { TransactionEventType, useAccount, useSpaceStationGalaxyConstants } from '@masknet/web3-shared-evm'
+import { useAsyncFn } from 'react-use'
 import Services from '../../../../extension/service'
+import type { CampaignInfo } from '../../types'
+import { getAccountClaimSignature, mutationParticipate } from '../../Worker/apis/spaceStationGalaxy'
+import { useSpaceStationContract } from './useSpaceStationContract'
 
 export function useSpaceStationContractClaimCallback(campaignInfo: CampaignInfo) {
     const account = useAccount()
     const spaceStationContract = useSpaceStationContract()
     const { CONTRACT_ADDRESS } = useSpaceStationGalaxyConstants()
-    const [claimState, setClaimState] = useTransactionState()
 
-    const claimCallback = useCallback(async () => {
+    return useAsyncFn(async () => {
         if (!CONTRACT_ADDRESS || !spaceStationContract || !campaignInfo) {
-            setClaimState({ type: TransactionStateType.UNKNOWN })
             return
         }
 
-        // start waiting for provider to confirm tx
-        setClaimState({
-            type: TransactionStateType.WAIT_FOR_CONFIRMING,
-        })
-
-        let useSignature = ''
-        try {
-            useSignature = await Services.Ethereum.personalSign(
-                `${campaignInfo.name}
+        const useSignature = await Services.Ethereum.personalSign(
+            `${campaignInfo.name}
 
 ${campaignInfo.description}`,
-                account,
-            )
-        } catch (error) {
-            setClaimState({
-                type: TransactionStateType.FAILED,
-                error: new Error('Not allowed to claim.'),
-            })
-        }
+            account,
+        )
 
         const { allow, signature, verifyIDs, nftCoreAddress, powahs } = await getAccountClaimSignature(
             useSignature,
@@ -53,10 +32,7 @@ ${campaignInfo.description}`,
         )
 
         if (!allow) {
-            setClaimState({
-                type: TransactionStateType.FAILED,
-                error: new Error('Not allowed to claim.'),
-            })
+            throw new Error('Not allowed to claim.')
         }
         const params = [campaignInfo.id, nftCoreAddress, verifyIDs[0], powahs[0], signature] as Parameters<
             SpaceStationGalaxy['methods']['claim']
@@ -64,15 +40,9 @@ ${campaignInfo.description}`,
         // estimate gas and compose transaction
         const config = {
             from: account,
-            gas: await spaceStationContract.methods
-                .claim(...params)
-                .estimateGas({ from: account })
-                .catch((error) => {
-                    setClaimState({ type: TransactionStateType.FAILED, error })
-                    throw error
-                }),
+            gas: await spaceStationContract.methods.claim(...params).estimateGas({ from: account }),
         }
-        return new Promise<void>(async (resolve, reject) => {
+        return new Promise<string>(async (resolve, reject) => {
             spaceStationContract.methods
                 .claim(...params)
                 .send(config as NonPayableTx)
@@ -87,39 +57,15 @@ ${campaignInfo.description}`,
                     )
 
                     if (!participated) {
-                        setClaimState({
-                            type: TransactionStateType.FAILED,
-                            error: new Error('Failed to claim'),
-                        })
+                        throw new Error('Failed to claim')
                     }
                 })
-                .on(TransactionEventType.RECEIPT, (receipt) => {
-                    setClaimState({
-                        type: TransactionStateType.CONFIRMED,
-                        no: 0,
-                        receipt,
-                    })
-                    resolve()
-                })
                 .on(TransactionEventType.CONFIRMATION, (no, receipt) => {
-                    setClaimState({
-                        type: TransactionStateType.CONFIRMED,
-                        no,
-                        receipt,
-                    })
-                    resolve()
+                    resolve(receipt.transactionHash)
                 })
                 .on(TransactionEventType.ERROR, (error) => {
-                    setClaimState({
-                        type: TransactionStateType.FAILED,
-                        error,
-                    })
                     reject(error)
                 })
         })
     }, [account, campaignInfo, CONTRACT_ADDRESS, spaceStationContract])
-
-    const resetCallback = useCallback(() => {}, [setClaimState])
-
-    return [claimState, claimCallback, resetCallback] as const
 }
diff --git a/packages/mask/src/plugins/MaskBox/SNSAdaptor/components/PreviewCard.tsx b/packages/mask/src/plugins/MaskBox/SNSAdaptor/components/PreviewCard.tsx
index faff29761645..bacbc617e7b8 100644
--- a/packages/mask/src/plugins/MaskBox/SNSAdaptor/components/PreviewCard.tsx
+++ b/packages/mask/src/plugins/MaskBox/SNSAdaptor/components/PreviewCard.tsx
@@ -97,7 +97,7 @@ export function PreviewCard(props: PreviewCardProps) {
     }, [openBoxTransaction?.config, openBoxTransactionOverrides, openBoxTransactionGasLimit])
 
     // #region open box
-    const [isOpening, openBoxCallback] = useTransactionCallback(txConfig, openBoxTransaction?.method)
+    const [{ loading: isOpening }, openBoxCallback] = useTransactionCallback(txConfig, openBoxTransaction?.method)
     const onRefresh = useCallback(() => {
         state[1](CardTab.Articles)
         setPaymentCount(1)
diff --git a/packages/mask/src/plugins/RedPacket/SNSAdaptor/NftList.tsx b/packages/mask/src/plugins/RedPacket/SNSAdaptor/NftList.tsx
index e26742b4bfd1..ba5f31bf2a4f 100644
--- a/packages/mask/src/plugins/RedPacket/SNSAdaptor/NftList.tsx
+++ b/packages/mask/src/plugins/RedPacket/SNSAdaptor/NftList.tsx
@@ -3,8 +3,8 @@ import { ERC721ContractDetailed, formatNFT_TokenId } from '@masknet/web3-shared-
 import { List, ListItem, ListProps, Typography } from '@mui/material'
 import classnames from 'classnames'
 import { FC, HTMLProps, useState } from 'react'
-import { useI18N } from '../../../utils'
 import { NFTCardStyledAssetPlayer } from '@masknet/shared'
+import { useI18N } from '../locales'
 
 const useStyles = makeStyles()((theme) => {
     const smallQuery = `@media (max-width: ${theme.breakpoints.values.sm}px)`
@@ -99,7 +99,7 @@ interface NftItemProps extends HTMLProps<HTMLDivElement> {
 }
 
 export const NftItem: FC<NftItemProps> = ({ contract, tokenId, className, claimed, renderOrder, ...rest }) => {
-    const { t } = useI18N()
+    const t = useI18N()
     const { classes } = useStyles()
     const [name, setName] = useState(formatNFT_TokenId(tokenId, 2))
 
@@ -116,7 +116,7 @@ export const NftItem: FC<NftItemProps> = ({ contract, tokenId, className, claime
                 setERC721TokenName={setName}
             />
             <Typography className={classes.name}>{name}</Typography>
-            {claimed && <Typography className={classes.claimedBadge}>{t('plugin_red_packet_claimed')}</Typography>}
+            {claimed && <Typography className={classes.claimedBadge}>{t.claimed()}</Typography>}
         </div>
     )
 }
diff --git a/packages/mask/src/plugins/RedPacket/SNSAdaptor/NftRedPacketHistoryItem.tsx b/packages/mask/src/plugins/RedPacket/SNSAdaptor/NftRedPacketHistoryItem.tsx
index 286a4d520610..1b767ead5a4f 100644
--- a/packages/mask/src/plugins/RedPacket/SNSAdaptor/NftRedPacketHistoryItem.tsx
+++ b/packages/mask/src/plugins/RedPacket/SNSAdaptor/NftRedPacketHistoryItem.tsx
@@ -1,5 +1,4 @@
 import { FC, memo, MouseEventHandler, useCallback } from 'react'
-import { Trans } from 'react-i18next'
 import classNames from 'classnames'
 import { fill } from 'lodash-unified'
 import { TokenIcon } from '@masknet/shared'
@@ -9,12 +8,12 @@ import { WalletMessages } from '@masknet/plugin-wallet'
 import { ERC721ContractDetailed, useAccount, useERC721ContractDetailed } from '@masknet/web3-shared-evm'
 import { Box, ListItem, Typography } from '@mui/material'
 import ActionButton from '../../../extension/options-page/DashboardComponents/ActionButton'
-import { useI18N } from '../../../utils/i18n-next-ui'
 import { dateTimeFormat } from '../../ITO/assets/formatDate'
 import type { NftRedPacketHistory } from '../types'
 import { useAvailabilityNftRedPacket } from './hooks/useAvailabilityNftRedPacket'
 import { useNftAvailabilityComputed } from './hooks/useNftAvailabilityComputed'
 import { NftList } from './NftList'
+import { Translate, useI18N } from '../locales'
 
 const useStyles = makeStyles()((theme) => {
     const smallQuery = `@media (max-width: ${theme.breakpoints.values.sm}px)`
@@ -150,7 +149,7 @@ export interface NftRedPacketHistoryItemProps {
 export const NftRedPacketHistoryItem: FC<NftRedPacketHistoryItemProps> = memo(
     ({ history, onSend, onShowPopover, onHidePopover }) => {
         const account = useAccount()
-        const { t } = useI18N()
+        const t = useI18N()
         const { classes } = useStyles()
         const {
             computed: { canSend, isPasswordValid },
@@ -175,7 +174,7 @@ export const NftRedPacketHistoryItem: FC<NftRedPacketHistoryItemProps> = memo(
             }
         }
         const handleShowPopover = (anchor: HTMLElement) => {
-            onShowPopover(anchor, t('plugin_nft_red_packet_data_broken'))
+            onShowPopover(anchor, t.nft_data_broken())
         }
 
         return (
@@ -196,10 +195,10 @@ export const NftRedPacketHistoryItem: FC<NftRedPacketHistoryItemProps> = memo(
                                 <Typography
                                     variant="body1"
                                     className={classNames(classes.title, classes.message, classes.ellipsis)}>
-                                    {history.message === '' ? t('plugin_red_packet_best_wishes') : history.message}
+                                    {history.message === '' ? t.best_wishes() : history.message}
                                 </Typography>
                                 <Typography variant="body1" className={classNames(classes.info, classes.message)}>
-                                    {t('plugin_red_packet_history_duration', {
+                                    {t.history_duration({
                                         startTime: dateTimeFormat(new Date(history.creation_time)),
                                         endTime: dateTimeFormat(
                                             new Date(history.creation_time + history.duration),
@@ -219,7 +218,7 @@ export const NftRedPacketHistoryItem: FC<NftRedPacketHistoryItemProps> = memo(
                                     )}
                                     variant="contained"
                                     size="large">
-                                    {t('plugin_red_packet_history_send')}
+                                    {t.send()}
                                 </ActionButton>
                             ) : null}
                         </section>
@@ -237,14 +236,13 @@ export const NftRedPacketHistoryItem: FC<NftRedPacketHistoryItemProps> = memo(
                         </section>
                         <section className={classes.footer}>
                             <Typography variant="body1" className={classes.footerInfo}>
-                                <Trans
-                                    i18nKey="plugin_red_packet_history_claimed"
+                                <Translate.history_claimed
                                     components={{
                                         strong: <strong />,
                                     }}
                                     values={{
-                                        claimedShares: history.claimers.length,
-                                        shares: history.shares,
+                                        claimedShares: history.claimers.length.toString(),
+                                        shares: history.shares.toString(),
                                     }}
                                 />
                             </Typography>
diff --git a/packages/mask/src/plugins/RedPacket/SNSAdaptor/NftRedPacketHistoryList.tsx b/packages/mask/src/plugins/RedPacket/SNSAdaptor/NftRedPacketHistoryList.tsx
index 16affe48056f..dc8e88b63923 100644
--- a/packages/mask/src/plugins/RedPacket/SNSAdaptor/NftRedPacketHistoryList.tsx
+++ b/packages/mask/src/plugins/RedPacket/SNSAdaptor/NftRedPacketHistoryList.tsx
@@ -7,7 +7,8 @@ import { useRef, useState } from 'react'
 import type { NftRedPacketHistory } from '../types'
 import { useNftRedPacketHistory } from './hooks/useNftRedPacketHistory'
 import { NftRedPacketHistoryItem } from './NftRedPacketHistoryItem'
-import { useI18N } from '../../../utils'
+import { useI18N as useBaseI18n } from '../../../utils'
+import { useI18N } from '../locales'
 
 const useStyles = makeStyles<void, 'atBottom'>()((theme, _, refs) => {
     const smallQuery = `@media (max-width: ${theme.breakpoints.values.sm}px)`
@@ -80,7 +81,8 @@ interface Props {
 
 export function NftRedPacketHistoryList({ onSend }: Props) {
     const { classes } = useStyles()
-    const { t } = useI18N()
+    const { t: tr } = useBaseI18n()
+    const t = useI18N()
     const account = useAccount()
     const chainId = useChainId()
     const { histories, fetchMore, loading } = useNftRedPacketHistory(account, chainId)
@@ -101,7 +103,7 @@ export function NftRedPacketHistoryList({ onSend }: Props) {
     if (chainId === ChainId.BSC) {
         return (
             <Typography className={classes.placeholder} color="textSecondary">
-                {t('plugin_chain_not_supported', { chain: 'Binance Smart Chain' })}
+                {t.chain_not_supported({ chain: 'Binance Smart Chain' })}
             </Typography>
         )
     }
@@ -109,7 +111,7 @@ export function NftRedPacketHistoryList({ onSend }: Props) {
     if (loading) {
         return (
             <Typography className={classes.placeholder} color="textSecondary">
-                {t('loading')}
+                {tr('loading')}
             </Typography>
         )
     }
@@ -117,7 +119,7 @@ export function NftRedPacketHistoryList({ onSend }: Props) {
     if (!histories?.length) {
         return (
             <Typography className={classes.placeholder} color="textSecondary">
-                {t('plugin_red_packet_nft_no_history')}
+                {t.nft_no_history()}
             </Typography>
         )
     }
diff --git a/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacket/OperationFooter.tsx b/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacket/OperationFooter.tsx
index 1fc627f16cf3..06e1d7d1f0b7 100644
--- a/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacket/OperationFooter.tsx
+++ b/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacket/OperationFooter.tsx
@@ -4,9 +4,10 @@ import { useRemoteControlledDialog } from '@masknet/shared-base-ui'
 import { ChainId, useAccount, useChainIdValid } from '@masknet/web3-shared-evm'
 import { Box, useTheme } from '@mui/material'
 import ActionButton from '../../../../extension/options-page/DashboardComponents/ActionButton'
-import { useI18N } from '../../../../utils'
 import { EthereumChainBoundary } from '../../../../web3/UI/EthereumChainBoundary'
+import { useI18N as useBaseI18n } from '../../../../utils'
 import { EthereumWalletConnectedBoundary } from '../../../../web3/UI/EthereumWalletConnectedBoundary'
+import { useI18N } from '../../locales'
 import { useStyles } from './useStyles'
 
 interface OperationFooterProps {
@@ -28,7 +29,8 @@ export function OperationFooter({
     onClaimOrRefund,
 }: OperationFooterProps) {
     const { classes } = useStyles()
-    const { t } = useI18N()
+    const { t: tr } = useBaseI18n()
+    const t = useI18N()
     const account = useAccount()
     const chainIdValid = useChainIdValid()
     const theme = useTheme()
@@ -45,14 +47,14 @@ export function OperationFooter({
         if (!account) {
             return (
                 <ActionButton variant="contained" fullWidth onClick={openSelectProviderDialog}>
-                    {t('plugin_wallet_connect_a_wallet')}
+                    {tr('plugin_wallet_connect_a_wallet')}
                 </ActionButton>
             )
         }
         if (!chainIdValid) {
             return (
                 <ActionButton disabled variant="contained" fullWidth>
-                    {t('plugin_wallet_invalid_network')}
+                    {tr('plugin_wallet_invalid_network')}
                 </ActionButton>
             )
         }
@@ -73,13 +75,7 @@ export function OperationFooter({
                 disabled={isLoading}
                 variant="contained"
                 onClick={onClaimOrRefund}>
-                {canClaim
-                    ? isClaiming
-                        ? t('plugin_red_packet_claiming')
-                        : t('plugin_red_packet_claim')
-                    : isRefunding
-                    ? t('plugin_red_packet_refunding')
-                    : t('plugin_red_packet_refund')}
+                {canClaim ? (isClaiming ? t.claiming() : t.claim()) : isRefunding ? t.refunding() : t.refund()}
             </ActionButton>
         )
     }
@@ -109,7 +105,7 @@ export function OperationFooter({
                                 startIcon={<SharedIcon style={{ fontSize: 18 }} />}
                                 variant="contained"
                                 onClick={onShare}>
-                                {t('share')}
+                                {tr('share')}
                             </ActionButton>
                         )}
                         <ObtainButton />
diff --git a/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacket/index.tsx b/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacket/index.tsx
index 57be9b6a6518..d1f52642f190 100644
--- a/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacket/index.tsx
+++ b/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacket/index.tsx
@@ -15,7 +15,8 @@ import { usePostLink } from '../../../../components/DataSource/usePostInfo'
 import { activatedSocialNetworkUI } from '../../../../social-network'
 import { isFacebook } from '../../../../social-network-adaptor/facebook.com/base'
 import { isTwitter } from '../../../../social-network-adaptor/twitter.com/base'
-import { useI18N } from '../../../../utils'
+import { useI18N as useBaseI18n } from '../../../../utils'
+import { useI18N } from '../../locales'
 import type { RedPacketAvailability, RedPacketJSONPayload } from '../../types'
 import { RedPacketStatus } from '../../types'
 import { useAvailabilityComputed } from '../hooks/useAvailabilityComputed'
@@ -31,7 +32,8 @@ export interface RedPacketProps {
 export function RedPacket(props: RedPacketProps) {
     const { payload } = props
 
-    const { t } = useI18N()
+    const t = useI18N()
+    const { t: tr } = useBaseI18n()
     const { classes } = useStyles()
 
     // context
@@ -54,12 +56,6 @@ export function RedPacket(props: RedPacketProps) {
 
     // #region remote controlled transaction dialog
     const postLink = usePostLink()
-    const shareTextOption = {
-        sender: payload.sender.name,
-        payload: postLink,
-        network: resolveNetworkName(networkType),
-        account: isTwitter(activatedSocialNetworkUI) ? t('twitter_account') : t('facebook_account'),
-    }
 
     const [{ loading: isClaiming, value: claimTxHash }, claimCallback] = useClaimCallback(
         payload.contract_version,
@@ -68,15 +64,25 @@ export function RedPacket(props: RedPacketProps) {
         payload.contract_version > 3 ? web3.eth.accounts.sign(account, payload.password).signature : payload.password,
     )
 
-    const shareText = (
-        listOfStatus.includes(RedPacketStatus.claimed) || claimTxHash
-            ? isTwitter(activatedSocialNetworkUI) || isFacebook(activatedSocialNetworkUI)
-                ? t('plugin_red_packet_share_message_official_account', shareTextOption)
-                : t('plugin_red_packet_share_message_not_twitter', shareTextOption)
-            : isTwitter(activatedSocialNetworkUI) || isFacebook(activatedSocialNetworkUI)
-            ? t('plugin_red_packet_share_unclaimed_message_official_account', shareTextOption)
-            : t('plugin_red_packet_share_unclaimed_message_not_twitter', shareTextOption)
-    ).trim()
+    const shareText = useMemo(() => {
+        const isOnTwitter = isTwitter(activatedSocialNetworkUI)
+        const isOnFacebook = isFacebook(activatedSocialNetworkUI)
+        const shareTextOption = {
+            sender: payload.sender.name,
+            payload: postLink.toString(),
+            network: resolveNetworkName(networkType),
+            account: isTwitter(activatedSocialNetworkUI) ? tr('twitter_account') : tr('facebook_account'),
+        }
+        if (listOfStatus.includes(RedPacketStatus.claimed) || claimTxHash) {
+            return isOnTwitter || isOnFacebook
+                ? t.share_message_official_account(shareTextOption)
+                : t.share_message_not_twitter(shareTextOption)
+        }
+
+        return isOnTwitter || isOnFacebook
+            ? t.share_unclaimed_message_official_account(shareTextOption)
+            : t.share_unclaimed_message_not_twitter(shareTextOption)
+    }, [payload, postLink, networkType, claimTxHash, listOfStatus, activatedSocialNetworkUI, t, tr])
 
     const [{ loading: isRefunding }, isRefunded, refundCallback] = useRefundCallback(
         payload.contract_version,
@@ -105,8 +111,7 @@ export function RedPacket(props: RedPacketProps) {
 
     const myStatus = useMemo(() => {
         if (token && listOfStatus.includes(RedPacketStatus.claimed))
-            return t(
-                'plugin_red_packet_description_claimed',
+            return t.description_claimed(
                 (availability as RedPacketAvailability).claimed_amount
                     ? {
                           amount: formatBalance(
@@ -114,9 +119,9 @@ export function RedPacket(props: RedPacketProps) {
                               token.decimals,
                               8,
                           ),
-                          symbol: token.symbol,
+                          symbol: token.symbol || '-',
                       }
-                    : { amount: '', symbol: '' },
+                    : { amount: '-', symbol: '-' },
             )
         return ''
     }, [listOfStatus, t, token])
@@ -125,18 +130,18 @@ export function RedPacket(props: RedPacketProps) {
         if (!availability || !token) return
 
         if (listOfStatus.includes(RedPacketStatus.expired) && canRefund)
-            return t('plugin_red_packet_description_refund', {
+            return t.description_refund({
                 balance: formatBalance(availability.balance, token.decimals),
-                symbol: token.symbol,
+                symbol: token.symbol ?? '-',
             })
-        if (listOfStatus.includes(RedPacketStatus.refunded)) return t('plugin_red_packet_description_refunded')
-        if (listOfStatus.includes(RedPacketStatus.expired)) return t('plugin_red_packet_description_expired')
-        if (listOfStatus.includes(RedPacketStatus.empty)) return t('plugin_red_packet_description_empty')
-        if (!payload.password) return t('plugin_red_packet_description_broken')
-        return t('plugin_red_packet_description_failover', {
+        if (listOfStatus.includes(RedPacketStatus.refunded)) return t.description_refunded()
+        if (listOfStatus.includes(RedPacketStatus.expired)) return t.description_expired()
+        if (listOfStatus.includes(RedPacketStatus.empty)) return t.description_empty()
+        if (!payload.password) return t.description_broken()
+        return t.description_failover({
             total: formatBalance(payload.total, token.decimals),
-            symbol: token.symbol,
-            shares: payload.shares ?? '-',
+            symbol: token.symbol ?? '-',
+            shares: payload.shares.toString() ?? '-',
         })
     }, [availability, canRefund, token, t, payload, listOfStatus])
 
@@ -150,7 +155,7 @@ export function RedPacket(props: RedPacketProps) {
         return (
             <Card className={classes.root} component="article" elevation={0}>
                 <Typography className={classes.loadingText} variant="body2">
-                    {t('loading')}
+                    {tr('loading')}
                 </Typography>
             </Card>
         )
@@ -178,7 +183,7 @@ export function RedPacket(props: RedPacketProps) {
                             {myStatus}
                         </Typography>
                         <Typography className={classes.from} variant="body1">
-                            {t('plugin_red_packet_from', { name: payload.sender.name ?? '-' })}
+                            {t.from({ name: payload.sender.name ?? '-' })}
                         </Typography>
                     </div>
                 </div>
diff --git a/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketConfirmDialog.tsx b/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketConfirmDialog.tsx
index 1bde8315ac8e..601ee15fcc2b 100644
--- a/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketConfirmDialog.tsx
+++ b/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketConfirmDialog.tsx
@@ -6,7 +6,6 @@ import {
     getChainName,
     isNativeTokenAddress,
     resolveTokenLinkOnExplorer,
-    TransactionStateType,
     useAccount,
     useChainId,
     useNetworkType,
@@ -18,10 +17,10 @@ import { makeStyles } from '@masknet/theme'
 import LaunchIcon from '@mui/icons-material/Launch'
 import { FormattedBalance, useOpenShareTxDialog } from '@masknet/shared'
 import ActionButton from '../../../extension/options-page/DashboardComponents/ActionButton'
-import { useI18N } from '../../../utils'
 import { RedPacketSettings, useCreateCallback } from './hooks/useCreateCallback'
 import type { RedPacketJSONPayload, RedPacketRecord } from '../types'
 import { RedPacketRPC } from '../messages'
+import { useI18N } from '../locales'
 
 const useStyles = makeStyles()((theme) => ({
     link: {
@@ -85,7 +84,7 @@ export interface ConfirmRedPacketFormProps {
 }
 
 export function RedPacketConfirmDialog(props: ConfirmRedPacketFormProps) {
-    const { t } = useI18N()
+    const t = useI18N()
     const { onBack, settings, onClose, onCreated } = props
     const { classes } = useStyles()
     const chainId = useChainId()
@@ -101,12 +100,7 @@ export function RedPacketConfirmDialog(props: ConfirmRedPacketFormProps) {
     const web3 = useWeb3()
     const account = useAccount()
     const { address: publicKey, privateKey } = useMemo(() => web3.eth.accounts.create(), [])
-    const [createState, createCallback, resetCreateCallback] = useCreateCallback(settings!, contract_version, publicKey)
-    const isCreating = [
-        TransactionStateType.WAIT_FOR_CONFIRMING,
-        TransactionStateType.HASH,
-        TransactionStateType.RECEIPT,
-    ].includes(createState.type)
+    const [{ loading: isCreating }, createCallback] = useCreateCallback(settings!, contract_version, publicKey)
     const openShareTxDialog = useOpenShareTxDialog()
     const createRedpacket = useCallback(async () => {
         const receipt = await createCallback()
@@ -114,8 +108,6 @@ export function RedPacketConfirmDialog(props: ConfirmRedPacketFormProps) {
         await openShareTxDialog({
             hash: receipt.transactionHash,
         })
-        // reset state
-        resetCreateCallback()
 
         // the settings is not available
         if (!settings?.token) return
@@ -141,9 +133,17 @@ export function RedPacketConfirmDialog(props: ConfirmRedPacketFormProps) {
         payload.current.creation_time = Number.parseInt(CreationSuccess.creation_time, 10) * 1000
         payload.current.token = settings.token
 
+        const record: RedPacketRecord = {
+            id: receipt.transactionHash!,
+            from: '',
+            password: privateKey,
+            contract_version,
+        }
+        RedPacketRPC.discoverRedPacket(record)
+
         // output the redpacket as JSON payload
         onCreated(payload.current)
-    }, [createCallback, resetCreateCallback, settings, openShareTxDialog, onCreated])
+    }, [createCallback, settings, openShareTxDialog, onCreated])
     // #endregion
 
     // assemble JSON payload
@@ -154,7 +154,6 @@ export function RedPacketConfirmDialog(props: ConfirmRedPacketFormProps) {
     const { HAPPY_RED_PACKET_ADDRESS_V4 } = useRedPacketConstants()
     const networkType = useNetworkType()
     useEffect(() => {
-        if (createState.type !== TransactionStateType.UNKNOWN) return
         const contractAddress = HAPPY_RED_PACKET_ADDRESS_V4
         if (!contractAddress) {
             onClose()
@@ -163,25 +162,8 @@ export function RedPacketConfirmDialog(props: ConfirmRedPacketFormProps) {
         payload.current.contract_address = contractAddress
         payload.current.contract_version = contract_version
         payload.current.network = getChainName(chainId)
-    }, [chainId, networkType, contract_version, createState])
+    }, [chainId, networkType, contract_version])
 
-    useEffect(() => {
-        if (!settings?.token || createState.type === TransactionStateType.UNKNOWN) return
-
-        // storing the created red packet in DB, it helps retrieve red packet password later
-        // save to the database early, otherwise red-packet would lose when close the tx dialog or
-        //  web page before create successfully.
-        if (createState.type === TransactionStateType.HASH && createState.hash) {
-            payload.current.txid = createState.hash
-            const record: RedPacketRecord = {
-                id: createState.hash!,
-                from: '',
-                password: privateKey,
-                contract_version,
-            }
-            RedPacketRPC.discoverRedPacket(record)
-        }
-    }, [createState /* update tx dialog only if state changed */])
     // #endregion
 
     return (
@@ -193,7 +175,7 @@ export function RedPacketConfirmDialog(props: ConfirmRedPacketFormProps) {
             </Grid>
             <Grid item xs={6}>
                 <Typography variant="body1" color="textSecondary">
-                    {t('plugin_red_packet_token')}
+                    {t.token()}
                 </Typography>
             </Grid>
             <Grid item xs={6}>
@@ -215,18 +197,18 @@ export function RedPacketConfirmDialog(props: ConfirmRedPacketFormProps) {
 
             <Grid item xs={6}>
                 <Typography variant="body1" color="textSecondary">
-                    {t('plugin_red_packet_split_mode')}
+                    {t.split_mode()}
                 </Typography>
             </Grid>
             <Grid item xs={6}>
                 <Typography variant="body1" color="textPrimary" align="right">
-                    {settings?.isRandom ? t('plugin_red_packet_random') : t('plugin_red_packet_average')}
+                    {settings?.isRandom ? t.random() : t.average()}
                 </Typography>
             </Grid>
 
             <Grid item xs={6}>
                 <Typography variant="body1" color="textSecondary">
-                    {t('plugin_red_packet_shares')}
+                    {t.shares()}
                 </Typography>
             </Grid>
             <Grid item xs={6}>
@@ -239,7 +221,7 @@ export function RedPacketConfirmDialog(props: ConfirmRedPacketFormProps) {
                 <>
                     <Grid item xs={6}>
                         <Typography variant="body1" color="textSecondary">
-                            {t('plugin_red_packet_amount_per_share')}
+                            {t.amount_per_share()}
                         </Typography>
                     </Grid>
                     <Grid item xs={6}>
@@ -257,7 +239,7 @@ export function RedPacketConfirmDialog(props: ConfirmRedPacketFormProps) {
 
             <Grid item xs={6}>
                 <Typography variant="body1" color="textSecondary">
-                    {t('plugin_red_packet_amount_total')}
+                    {t.amount_total()}
                 </Typography>
             </Grid>
             <Grid item xs={6}>
@@ -273,21 +255,21 @@ export function RedPacketConfirmDialog(props: ConfirmRedPacketFormProps) {
             <Grid item xs={12}>
                 <Paper className={classes.hit}>
                     <Typography variant="body1" align="center" style={{ fontSize: 14, lineHeight: '20px' }}>
-                        {t('plugin_red_packet_hint')}
+                        {t.hint()}
                     </Typography>
                 </Paper>
             </Grid>
 
             <Grid item xs={6}>
                 <ActionButton disabled={isCreating} variant="contained" size="large" fullWidth onClick={onBack}>
-                    {t('plugin_red_packet_back')}
+                    {t.back()}
                 </ActionButton>
             </Grid>
             <Grid item xs={6}>
                 <ActionButton loading={isCreating} variant="contained" size="large" fullWidth onClick={createRedpacket}>
-                    {t('plugin_red_packet_send_symbol', {
+                    {t.send_symbol({
                         amount: formatBalance(settings?.total, settings?.token?.decimals ?? 0),
-                        symbol: settings?.token?.symbol,
+                        symbol: settings?.token?.symbol ?? '-',
                     })}
                 </ActionButton>
             </Grid>
diff --git a/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketCreateNew.tsx b/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketCreateNew.tsx
index cdc94626b9c1..3ceac4ff3cbd 100644
--- a/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketCreateNew.tsx
+++ b/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketCreateNew.tsx
@@ -3,7 +3,7 @@ import { useChainId, ChainId } from '@masknet/web3-shared-evm'
 import { RedPacketFormProps, RedPacketERC20Form } from './RedPacketERC20Form'
 import { RedPacketERC721Form } from './RedPacketERC721Form'
 import AbstractTab, { AbstractTabProps } from '../../../components/shared/AbstractTab'
-import { useI18N } from '../../../utils'
+import { useI18N } from '../locales'
 import { activatedSocialNetworkUI } from '../../../social-network'
 import { IconURLs } from './IconURL'
 import { EnhanceableSite } from '@masknet/shared-base'
@@ -49,7 +49,7 @@ const useStyles = makeStyles<StyleProps>()((theme, { snsId }) => ({
 
 export function RedPacketCreateNew(props: RedPacketFormProps & { state: readonly [number, (next: number) => void] }) {
     const { origin, onNext, onChange, onClose, state } = props
-    const { t } = useI18N()
+    const t = useI18N()
     const { classes } = useStyles({ snsId: activatedSocialNetworkUI.networkIdentifier })
     const chainId = useChainId()
 
@@ -59,7 +59,7 @@ export function RedPacketCreateNew(props: RedPacketFormProps & { state: readonly
                 label: (
                     <div className={classes.labelWrapper}>
                         <img className={classes.img} src={IconURLs.erc20Token} />
-                        <span>{t('plugin_red_packet_erc20_tab_title')}</span>
+                        <span>{t.erc20_tab_title()}</span>
                     </div>
                 ),
                 children: <RedPacketERC20Form origin={origin} onClose={onClose} onNext={onNext} onChange={onChange} />,
@@ -69,7 +69,7 @@ export function RedPacketCreateNew(props: RedPacketFormProps & { state: readonly
                 label: (
                     <div className={classes.labelWrapper}>
                         <img className={classes.img} src={IconURLs.erc721Token} />
-                        <span>{t('plugin_red_packet_erc721_tab_title')}</span>
+                        <span>{t.erc721_tab_title()}</span>
                     </div>
                 ),
                 children: <RedPacketERC721Form onClose={onClose} />,
diff --git a/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketDialog.tsx b/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketDialog.tsx
index cdadc425c58d..5e4e92c5d94a 100644
--- a/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketDialog.tsx
+++ b/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketDialog.tsx
@@ -13,7 +13,7 @@ import {
 } from '../../../components/DataSource/useActivatedUI'
 import AbstractTab, { AbstractTabProps } from '../../../components/shared/AbstractTab'
 import Services from '../../../extension/service'
-import { useI18N } from '../../../utils'
+import { useI18N } from '../locales'
 import { WalletMessages } from '../../Wallet/messages'
 import { RedPacketMetaKey } from '../constants'
 import { DialogTabs, RedPacketJSONPayload, RpTypeTabs } from '../types'
@@ -63,7 +63,7 @@ interface RedPacketDialogProps extends withClasses<never> {
 }
 
 export default function RedPacketDialog(props: RedPacketDialogProps) {
-    const { t } = useI18N()
+    const t = useI18N()
     const chainId = useChainId()
     const account = useAccount()
     const { classes } = useStyles()
@@ -134,7 +134,7 @@ export default function RedPacketDialog(props: RedPacketDialogProps) {
     const tabProps: AbstractTabProps = {
         tabs: [
             {
-                label: t('plugin_red_packet_create_new'),
+                label: t.create_new(),
                 children: (
                     <RedPacketCreateNew
                         origin={settings}
@@ -147,7 +147,7 @@ export default function RedPacketDialog(props: RedPacketDialogProps) {
                 sx: { p: 0 },
             },
             {
-                label: t('plugin_red_packet_select_existing'),
+                label: t.select_existing(),
                 children: <RedPacketPast onSelect={onCreateOrSelect} onClose={onClose} />,
                 sx: { p: 0 },
             },
@@ -172,7 +172,7 @@ export default function RedPacketDialog(props: RedPacketDialogProps) {
     )
 
     const isCreateStep = step === CreateRedPacketPageStep.NewRedPacketPage
-    const title = isCreateStep ? t('plugin_red_packet_display_name') : t('plugin_red_packet_details')
+    const title = isCreateStep ? t.display_name() : t.details()
 
     return (
         <InjectedDialog open={props.open} title={title} onClose={onClose} disableTitleBorder>
diff --git a/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketERC20Form.tsx b/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketERC20Form.tsx
index a4843e141d93..1055ed3cbb27 100644
--- a/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketERC20Form.tsx
+++ b/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketERC20Form.tsx
@@ -17,7 +17,8 @@ import { ChangeEvent, useCallback, useEffect, useMemo, useRef, useState } from '
 import { usePickToken } from '@masknet/shared'
 import { useCurrentIdentity, useCurrentLinkedPersona } from '../../../components/DataSource/useActivatedUI'
 import ActionButton from '../../../extension/options-page/DashboardComponents/ActionButton'
-import { useI18N } from '../../../utils'
+import { useI18N } from '../locales'
+import { useI18N as useBaseI18n } from '../../../utils'
 import { EthereumERC20TokenApprovedBoundary } from '../../../web3/UI/EthereumERC20TokenApprovedBoundary'
 import { EthereumWalletConnectedBoundary } from '../../../web3/UI/EthereumWalletConnectedBoundary'
 import { TokenAmountPanel } from '../../../web3/UI/TokenAmountPanel'
@@ -82,7 +83,8 @@ export interface RedPacketFormProps extends withClasses<never> {
 }
 
 export function RedPacketERC20Form(props: RedPacketFormProps) {
-    const { t } = useI18N()
+    const t = useI18N()
+    const { t: tr } = useBaseI18n()
     const classes = useStylesExtends(useStyles(), props)
     const { onChange, onNext, origin } = props
     // context
@@ -105,7 +107,7 @@ export function RedPacketERC20Form(props: RedPacketFormProps) {
 
     // #region packet settings
     const [isRandom, setRandom] = useState(origin?.isRandom ? 1 : 0)
-    const [message, setMessage] = useState(origin?.message || t('plugin_red_packet_best_wishes'))
+    const [message, setMessage] = useState(origin?.message || t.best_wishes())
     const currentIdentity = useCurrentIdentity()
 
     const { value: linkedPersona } = useCurrentLinkedPersona()
@@ -153,32 +155,32 @@ export function RedPacketERC20Form(props: RedPacketFormProps) {
     // #endregion
 
     const validationMessage = useMemo(() => {
-        if (!token) return t('plugin_wallet_select_a_token')
-        if (!account) return t('plugin_wallet_connect_a_wallet')
+        if (!token) return t.select_a_token()
+        if (!account) return tr('plugin_wallet_connect_a_wallet')
         if (isZero(shares || '0')) return 'Enter shares'
         if (isGreaterThan(shares || '0', 255)) return 'At most 255 recipients'
-        if (isZero(amount)) return t('plugin_dhedge_enter_an_amount')
+        if (isZero(amount)) return tr('plugin_dhedge_enter_an_amount')
         if (isGreaterThan(totalAmount, tokenBalance))
-            return t('plugin_gitcoin_insufficient_balance', { symbol: token.symbol })
+            return tr('plugin_gitcoin_insufficient_balance', { symbol: token.symbol })
         if (!isDivisible)
-            return t('plugin_red_packet_indivisible', {
-                symbol: token.symbol,
+            return t.indivisible({
+                symbol: token.symbol!,
                 amount: formatBalance(1, token.decimals),
             })
         return ''
-    }, [account, amount, totalAmount, shares, token, tokenBalance])
+    }, [account, amount, totalAmount, shares, token, tokenBalance, t, tr])
 
     const creatingParams = useMemo(
         () => ({
             duration,
             isRandom: Boolean(isRandom),
             name: senderName,
-            message: message || t('plugin_red_packet_best_wishes'),
+            message: message || t.best_wishes(),
             shares: shares || 0,
             token: token ? (omit(token, ['logoURI']) as FungibleTokenDetailed) : undefined,
             total: totalAmount.toFixed(),
         }),
-        [isRandom, senderName, message, t('plugin_red_packet_best_wishes'), shares, token, totalAmount],
+        [isRandom, senderName, message, t.best_wishes(), shares, token, totalAmount],
     )
 
     const onClick = useCallback(() => {
@@ -193,7 +195,7 @@ export function RedPacketERC20Form(props: RedPacketFormProps) {
         <>
             <div className={classes.field}>
                 <FormControl className={classes.input} variant="outlined">
-                    <InputLabel className={classes.selectShrinkLabel}>{t('plugin_red_packet_split_mode')}</InputLabel>
+                    <InputLabel className={classes.selectShrinkLabel}>{t.split_mode()}</InputLabel>
                     <Select
                         ref={selectRef}
                         value={isRandom ? 1 : 0}
@@ -211,8 +213,8 @@ export function RedPacketERC20Form(props: RedPacketFormProps) {
                             container: selectRef.current,
                             anchorEl: selectRef.current,
                         }}>
-                        <MenuItem value={0}>{t('plugin_red_packet_average')}</MenuItem>
-                        <MenuItem value={1}>{t('plugin_red_packet_random')}</MenuItem>
+                        <MenuItem value={0}>{t.average()}</MenuItem>
+                        <MenuItem value={1}>{t.random()}</MenuItem>
                     </Select>
                 </FormControl>
                 <TextField
@@ -233,7 +235,7 @@ export function RedPacketERC20Form(props: RedPacketFormProps) {
                             shrink: classes.inputShrinkLabel,
                         },
                     }}
-                    label={t('plugin_red_packet_shares')}
+                    label={t.shares()}
                     value={shares}
                     onChange={onShareChange}
                 />
@@ -241,7 +243,7 @@ export function RedPacketERC20Form(props: RedPacketFormProps) {
             <div className={classes.field}>
                 <TokenAmountPanel
                     classes={{ root: classes.input }}
-                    label={isRandom ? 'Total Amount' : t('plugin_red_packet_amount_per_share')}
+                    label={isRandom ? 'Total Amount' : t.amount_per_share()}
                     amount={rawAmount}
                     balance={tokenBalance}
                     token={token}
@@ -265,8 +267,8 @@ export function RedPacketERC20Form(props: RedPacketFormProps) {
                             shrink: classes.inputShrinkLabel,
                         },
                     }}
-                    inputProps={{ placeholder: t('plugin_red_packet_best_wishes') }}
-                    label={t('plugin_red_packet_attached_message')}
+                    inputProps={{ placeholder: t.best_wishes() }}
+                    label={t.attached_message()}
                     value={message}
                 />
             </div>
@@ -282,7 +284,7 @@ export function RedPacketERC20Form(props: RedPacketFormProps) {
                         fullWidth
                         disabled={!!validationMessage}
                         onClick={onClick}>
-                        {validationMessage || t('plugin_red_packet_next')}
+                        {validationMessage || t.next()}
                     </ActionButton>
                 </EthereumERC20TokenApprovedBoundary>
             </EthereumWalletConnectedBoundary>
diff --git a/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketERC721Form.tsx b/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketERC721Form.tsx
index 63c55e2e534b..b53e3f69a5da 100644
--- a/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketERC721Form.tsx
+++ b/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketERC721Form.tsx
@@ -1,7 +1,7 @@
 import { Box, Typography, List, ListItem, CircularProgress } from '@mui/material'
 import { makeStyles } from '@masknet/theme'
 import { useState, useCallback, useEffect, useMemo } from 'react'
-import { useI18N } from '../../../utils'
+import { useI18N } from '../locales'
 import classNames from 'classnames'
 import ActionButton from '../../../extension/options-page/DashboardComponents/ActionButton'
 import { ERC721ContractSelectPanel } from '../../../web3/UI/ERC721ContractSelectPanel'
@@ -201,7 +201,7 @@ interface RedPacketERC721FormProps {
     onClose: () => void
 }
 export function RedPacketERC721Form(props: RedPacketERC721FormProps) {
-    const { t } = useI18N()
+    const t = useI18N()
     const { onClose } = props
     const { classes } = useStyles()
     const [open, setOpen] = useState(false)
@@ -259,10 +259,10 @@ export function RedPacketERC721Form(props: RedPacketERC721FormProps) {
     const { RED_PACKET_NFT_ADDRESS } = useNftRedPacketConstants()
 
     const validationMessage = useMemo(() => {
-        if (!balance) return t('plugin_red_packet_erc721_insufficient_balance')
-        if (tokenDetailedList.length === 0) return t('plugin_wallet_select_a_token')
+        if (!balance) return t.erc721_insufficient_balance()
+        if (tokenDetailedList.length === 0) return t.select_a_token()
         return ''
-    }, [tokenDetailedList.length, balance])
+    }, [tokenDetailedList.length, balance, t])
 
     return (
         <>
@@ -297,8 +297,11 @@ export function RedPacketERC721Form(props: RedPacketERC721FormProps) {
                                 <Typography color="textPrimary">
                                     {tokenDetailedOwnerList.length === 0
                                         ? 'All'
-                                        : t('plugin_red_packet_nft_select_all_option', {
-                                              total: Math.min(NFT_RED_PACKET_MAX_SHARES, tokenDetailedOwnerList.length),
+                                        : t.nft_select_all_option({
+                                              total: Math.min(
+                                                  NFT_RED_PACKET_MAX_SHARES,
+                                                  tokenDetailedOwnerList.length,
+                                              ).toString(),
                                           })}
                                 </Typography>
                             </div>
@@ -310,9 +313,7 @@ export function RedPacketERC721Form(props: RedPacketERC721FormProps) {
                                     )}>
                                     <CheckIcon className={classes.checkIcon} />
                                 </div>
-                                <Typography color="textPrimary">
-                                    {t('plugin_red_packet_nft_select_partially_option')}
-                                </Typography>
+                                <Typography color="textPrimary">{t.nft_select_partially_option()}</Typography>
                             </div>
                         </Box>
                     )
@@ -338,12 +339,8 @@ export function RedPacketERC721Form(props: RedPacketERC721FormProps) {
                 </div>
                 {contract && balance && !loadingOwnerList ? (
                     <>
-                        <Typography className={classes.unapprovedTip}>
-                            {t('plugin_red_packet_nft_unapproved_tip')}
-                        </Typography>
-                        <Typography className={classes.approveAllTip}>
-                            {t('plugin_red_packet_nft_approve_all_tip')}
-                        </Typography>
+                        <Typography className={classes.unapprovedTip}>{t.nft_unapproved_tip()}</Typography>
+                        <Typography className={classes.approveAllTip}>{t.nft_approve_all_tip()}</Typography>
                     </>
                 ) : null}
                 <EthereumWalletConnectedBoundary>
@@ -358,7 +355,7 @@ export function RedPacketERC721Form(props: RedPacketERC721FormProps) {
                             disabled={!!validationMessage}
                             fullWidth
                             onClick={() => setOpenConfirmDialog(true)}>
-                            {t('plugin_red_packet_next')}
+                            {t.next()}
                         </ActionButton>
                     </EthereumERC721TokenApprovedBoundary>
                 </EthereumWalletConnectedBoundary>
diff --git a/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketInHistoryList.tsx b/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketInHistoryList.tsx
index 3e0749c9e2fc..1a2595f81691 100644
--- a/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketInHistoryList.tsx
+++ b/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketInHistoryList.tsx
@@ -18,9 +18,8 @@ import intervalToDuration from 'date-fns/intervalToDuration'
 import nextDay from 'date-fns/nextDay'
 import { omit, pick } from 'lodash-unified'
 import { MouseEvent, useCallback, useState } from 'react'
-import { Trans } from 'react-i18next'
 import ActionButton from '../../../extension/options-page/DashboardComponents/ActionButton'
-import { useI18N } from '../../../utils'
+import { Translate, useI18N } from '../locales'
 import { dateTimeFormat } from '../../ITO/assets/formatDate'
 import { StyledLinearProgress } from '../../ITO/SNSAdaptor/StyledLinearProgress'
 import { RedPacketJSONPayload, RedPacketJSONPayloadFromChain, RedPacketStatus } from '../types'
@@ -185,7 +184,7 @@ export interface RedPacketInHistoryListProps {
 export function RedPacketInHistoryList(props: RedPacketInHistoryListProps) {
     const account = useAccount()
     const { history, onSelect } = props
-    const { t } = useI18N()
+    const t = useI18N()
     const { classes } = useStyles()
     const isSmall = useMediaQuery((theme: Theme) => theme.breakpoints.down('sm'))
     const {
@@ -246,28 +245,24 @@ export function RedPacketInHistoryList(props: RedPacketInHistoryListProps) {
                         <div className={classes.div}>
                             <div className={classes.fullWidthBox}>
                                 <Typography variant="body1" className={classNames(classes.title, classes.message)}>
-                                    {history.sender.message === ''
-                                        ? t('plugin_red_packet_best_wishes')
-                                        : history.sender.message}
+                                    {history.sender.message === '' ? t.best_wishes() : history.sender.message}
                                 </Typography>
                             </div>
                             <Typography variant="body1" className={classNames(classes.info, classes.message)}>
-                                {t('plugin_red_packet_history_duration', {
+                                {t.history_duration({
                                     startTime: dateTimeFormat(new Date(history.creation_time)),
                                     endTime: dateTimeFormat(new Date(history.creation_time + history.duration), false),
                                 })}
                             </Typography>
                             <Typography variant="body1" className={classNames(classes.info, classes.message)}>
-                                {t('plugin_red_packet_history_total_amount', {
+                                {t.history_total_amount({
                                     amount: formatBalance(history.total, historyToken?.decimals, 6),
-                                    symbol: historyToken?.symbol,
+                                    symbol: historyToken?.symbol!,
                                 })}
                             </Typography>
                             <Typography variant="body1" className={classNames(classes.info, classes.message)}>
-                                {t('plugin_red_packet_history_split_mode', {
-                                    mode: history.is_random
-                                        ? t('plugin_red_packet_random')
-                                        : t('plugin_red_packet_average'),
+                                {t.history_split_mode({
+                                    mode: history.is_random ? t.random() : t.average(),
                                 })}
                             </Typography>
                         </div>
@@ -291,12 +286,12 @@ export function RedPacketInHistoryList(props: RedPacketInHistoryListProps) {
                                     variant="contained"
                                     size="large">
                                     {canSend
-                                        ? t('plugin_red_packet_history_send')
+                                        ? t.send()
                                         : refunded
-                                        ? t('plugin_red_packet_refunding')
+                                        ? t.refunding()
                                         : listOfStatus.includes(RedPacketStatus.empty)
-                                        ? t('plugin_red_packet_empty')
-                                        : t('plugin_red_packet_refund')}
+                                        ? t.empty()
+                                        : t.refund()}
                                 </ActionButton>
                                 <Popper
                                     className={classes.popper}
@@ -306,7 +301,7 @@ export function RedPacketInHistoryList(props: RedPacketInHistoryListProps) {
                                     transition
                                     disablePortal>
                                     <Typography className={classes.popperText}>
-                                        {t('plugin_red_packet_data_broken', { duration: formatRefundDuration })}
+                                        {t.data_broken({ duration: formatRefundDuration })}
                                     </Typography>
                                     <div className={classes.arrow} />
                                 </Popper>
@@ -319,20 +314,18 @@ export function RedPacketInHistoryList(props: RedPacketInHistoryListProps) {
                     />
                     <section className={classes.footer}>
                         <Typography variant="body1" className={classes.footerInfo}>
-                            <Trans
-                                i18nKey="plugin_red_packet_history_claimed"
+                            <Translate.history_claimed
                                 components={{
                                     strong: <strong />,
                                 }}
                                 values={{
-                                    claimedShares: history.claimers?.length ?? 0,
-                                    shares: history.shares,
+                                    claimedShares: history.claimers?.length.toString() ?? '0',
+                                    shares: history.shares.toString(),
                                 }}
                             />
                         </Typography>
                         <Typography variant="body1" className={classes.footerInfo}>
-                            <Trans
-                                i18nKey="plugin_red_packet_history_total_claimed_amount"
+                            <Translate.history_total_claimed_amount
                                 components={{
                                     strong: <strong className={classes.strong} />,
                                     span: <span className={classes.span} />,
@@ -344,7 +337,7 @@ export function RedPacketInHistoryList(props: RedPacketInHistoryListProps) {
                                         historyToken?.decimals,
                                         6,
                                     ),
-                                    symbol: historyToken?.symbol,
+                                    symbol: historyToken?.symbol!,
                                 }}
                             />
                         </Typography>
diff --git a/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketNft.tsx b/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketNft.tsx
index 3db5fa3656c9..62b11814cbc8 100644
--- a/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketNft.tsx
+++ b/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketNft.tsx
@@ -1,30 +1,31 @@
+import { PluginWalletConnectIcon, SharedIcon } from '@masknet/icons'
+import { NFTCardStyledAssetPlayer } from '@masknet/shared'
+import { openWindow } from '@masknet/shared-base-ui'
 import { makeStyles } from '@masknet/theme'
 import {
-    useAccount,
     resolveAddressLinkOnExplorer,
-    useWeb3,
     resolveNetworkName,
-    useNetworkType,
     TransactionStateType,
+    useAccount,
+    useNetworkType,
+    useWeb3,
 } from '@masknet/web3-shared-evm'
 import LaunchIcon from '@mui/icons-material/Launch'
-import { Card, CardHeader, Typography, Link, CardMedia, CardContent, Button, Box, Skeleton } from '@mui/material'
-import { useCallback, useEffect, useState } from 'react'
-import ActionButton from '../../../extension/options-page/DashboardComponents/ActionButton'
-import { useI18N } from '../../../utils'
-import { EthereumWalletConnectedBoundary } from '../../../web3/UI/EthereumWalletConnectedBoundary'
-import type { RedPacketNftJSONPayload } from '../types'
-import { useClaimNftRedpacketCallback } from './hooks/useClaimNftRedpacketCallback'
-import { useAvailabilityNftRedPacket } from './hooks/useAvailabilityNftRedPacket'
+import { Box, Button, Card, CardContent, CardHeader, CardMedia, Link, Skeleton, Typography } from '@mui/material'
 import classNames from 'classnames'
+import { useCallback, useEffect, useMemo, useState } from 'react'
 import { usePostLink } from '../../../components/DataSource/usePostInfo'
+import ActionButton from '../../../extension/options-page/DashboardComponents/ActionButton'
 import { activatedSocialNetworkUI } from '../../../social-network'
-import { isTwitter } from '../../../social-network-adaptor/twitter.com/base'
 import { isFacebook } from '../../../social-network-adaptor/facebook.com/base'
-import { NFTCardStyledAssetPlayer } from '@masknet/shared'
-import { openWindow } from '@masknet/shared-base-ui'
-import { PluginWalletConnectIcon, SharedIcon } from '@masknet/icons'
+import { isTwitter } from '../../../social-network-adaptor/twitter.com/base'
+import { useI18N as useBaseI18N } from '../../../utils'
 import { EthereumChainBoundary } from '../../../web3/UI/EthereumChainBoundary'
+import { EthereumWalletConnectedBoundary } from '../../../web3/UI/EthereumWalletConnectedBoundary'
+import { useI18N } from '../locales'
+import type { RedPacketNftJSONPayload } from '../types'
+import { useAvailabilityNftRedPacket } from './hooks/useAvailabilityNftRedPacket'
+import { useClaimNftRedpacketCallback } from './hooks/useClaimNftRedpacketCallback'
 
 const useStyles = makeStyles()((theme) => ({
     root: {
@@ -249,7 +250,8 @@ export interface RedPacketNftProps {
 }
 
 export function RedPacketNft({ payload }: RedPacketNftProps) {
-    const { t } = useI18N()
+    const { t: i18n } = useBaseI18N()
+    const t = useI18N()
     const { classes } = useStyles()
     const web3 = useWeb3()
     const account = useAccount()
@@ -295,29 +297,23 @@ export function RedPacketNft({ payload }: RedPacketNftProps) {
     // #region on share
     const postLink = usePostLink()
     const networkType = useNetworkType()
-    const shareText = availability?.isClaimed
-        ? t(
-              isTwitter(activatedSocialNetworkUI) || isFacebook(activatedSocialNetworkUI)
-                  ? 'plugin_red_packet_nft_share_claimed_message'
-                  : 'plugin_red_packet_nft_share_claimed_message_not_twitter',
-              {
-                  sender: payload.senderName,
-                  payload: postLink,
-                  network: resolveNetworkName(networkType),
-                  account: isTwitter(activatedSocialNetworkUI) ? t('twitter_account') : t('facebook_account'),
-              },
-          ).trim()
-        : t(
-              isTwitter(activatedSocialNetworkUI) || isFacebook(activatedSocialNetworkUI)
-                  ? 'plugin_red_packet_nft_share_foreshow_message'
-                  : 'plugin_red_packet_nft_share_foreshow_message_not_twitter',
-              {
-                  sender: payload.senderName,
-                  payload: postLink,
-                  network: resolveNetworkName(networkType),
-                  account: isTwitter(activatedSocialNetworkUI) ? t('twitter_account') : t('facebook_account'),
-              },
-          ).trim()
+    const shareText = useMemo(() => {
+        const isTwitterOrFacebook = isTwitter(activatedSocialNetworkUI) || isFacebook(activatedSocialNetworkUI)
+        const options = {
+            sender: payload.senderName,
+            payload: postLink.toString(),
+            network: resolveNetworkName(networkType),
+            account: isTwitter(activatedSocialNetworkUI) ? i18n('twitter_account') : i18n('facebook_account'),
+        }
+        if (availability?.isClaimed) {
+            return isTwitterOrFacebook
+                ? t.nft_share_claimed_message(options)
+                : t.nft_share_claimed_message_not_twitter(options)
+        }
+        return isTwitterOrFacebook
+            ? t.nft_share_foreshow_message(options)
+            : t.nft_share_foreshow_message_not_twitter(options)
+    }, [availability?.isClaimed, t, i18n])
 
     const onShare = useCallback(() => {
         if (shareText) activatedSocialNetworkUI.utils.share?.(shareText)
@@ -330,13 +326,13 @@ export function RedPacketNft({ payload }: RedPacketNftProps) {
                 <Card className={classNames(classes.card, classes.errorCard)} component="article" elevation={0}>
                     <img className={classes.errImage} src={rpNftImg} />
                     <Typography className={classes.whiteText} variant="h5">
-                        {t('loading_failed')}
+                        {i18n('loading_failed')}
                     </Typography>
                     <Button
                         onClick={retryAvailability}
                         className={classNames(classes.errorButton, classes.whiteText)}
                         variant="outlined">
-                        {t('try_again')}
+                        {i18n('try_again')}
                     </Button>
                 </Card>
             </div>
@@ -389,14 +385,14 @@ export function RedPacketNft({ payload }: RedPacketNftProps) {
                 ) : (
                     <CardMedia className={classes.image} component="div" image={rpNftImg}>
                         <Typography className={classes.remain}>
-                            {availability.claimedAmount}/{availability.totalAmount} {t('collectibles_name')}
+                            {availability.claimedAmount}/{availability.totalAmount} {i18n('collectibles_name')}
                         </Typography>
                     </CardMedia>
                 )}
 
                 <CardContent>
                     <Typography variant="body1" className={classes.whiteText}>
-                        {t('plugin_red_packet_nft_tip')}
+                        {t.nft_tip()}
                     </Typography>
                 </CardContent>
                 <div className={classes.footer}>
@@ -426,7 +422,7 @@ export function RedPacketNft({ payload }: RedPacketNftProps) {
                     />
                     <div className={classNames(classes.badge, classes.whiteText)}>
                         <Typography variant="body2" className={classes.badgeText}>
-                            {availability.expired ? t('plugin_red_packet_expired') : t('plugin_red_packet_completed')}
+                            {availability.expired ? t.expired() : t.completed()}
                         </Typography>
                     </div>
                 </Card>
@@ -439,7 +435,7 @@ export function RedPacketNft({ payload }: RedPacketNftProps) {
                             fullWidth
                             onClick={onShare}
                             variant="contained">
-                            {t('share')}
+                            {i18n('share')}
                         </Button>
                     </Box>
                     {availability.isClaimed ? null : (
@@ -460,7 +456,7 @@ export function RedPacketNft({ payload }: RedPacketNftProps) {
                                         onClick={claimCallback}
                                         className={classes.button}
                                         fullWidth>
-                                        {isClaiming ? t('plugin_red_packet_claiming') : t('plugin_red_packet_claim')}
+                                        {isClaiming ? t.claiming() : t.claim()}
                                     </ActionButton>
                                 </EthereumWalletConnectedBoundary>
                             </EthereumChainBoundary>
diff --git a/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketPast.tsx b/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketPast.tsx
index 644196520eae..40f166399cf0 100644
--- a/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketPast.tsx
+++ b/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedPacketPast.tsx
@@ -2,7 +2,7 @@ import { useChainId, ChainId } from '@masknet/web3-shared-evm'
 import { makeStyles } from '@masknet/theme'
 import { useCallback, useState } from 'react'
 import AbstractTab, { AbstractTabProps } from '../../../components/shared/AbstractTab'
-import { useI18N } from '../../../utils'
+import { useI18N } from '../locales'
 import { IconURLs } from './IconURL'
 import { RedPacketHistoryList } from './RedPacketHistoryList'
 import { NftRedPacketHistoryList } from './NftRedPacketHistoryList'
@@ -58,7 +58,7 @@ interface Props {
 }
 
 export function RedPacketPast({ onSelect, onClose }: Props) {
-    const { t } = useI18N()
+    const t = useI18N()
     const { classes } = useStyles()
     const state = useState(RpTypeTabs.ERC20)
     const chainId = useChainId()
@@ -95,7 +95,7 @@ export function RedPacketPast({ onSelect, onClose }: Props) {
                 label: (
                     <div className={classes.labelWrapper}>
                         <img className={classes.img} src={IconURLs.erc20Token} />
-                        <span>{t('plugin_red_packet_erc20_tab_title')}</span>
+                        <span>{t.erc20_tab_title()}</span>
                     </div>
                 ),
                 children: <RedPacketHistoryList onSelect={onSelect} />,
@@ -105,7 +105,7 @@ export function RedPacketPast({ onSelect, onClose }: Props) {
                 label: (
                     <div className={classes.labelWrapper}>
                         <img className={classes.img} src={IconURLs.erc721Token} />
-                        <span>{t('plugin_red_packet_erc721_tab_title')}</span>
+                        <span>{t.erc721_tab_title()}</span>
                     </div>
                 ),
                 children: <NftRedPacketHistoryList onSend={handleSendNftRedpacket} />,
diff --git a/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedpacketMessagePanel.tsx b/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedpacketMessagePanel.tsx
index 7270b5f44c14..86b54fb72449 100644
--- a/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedpacketMessagePanel.tsx
+++ b/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedpacketMessagePanel.tsx
@@ -1,7 +1,7 @@
 import classNames from 'classnames'
 import { Box, InputBase, Typography } from '@mui/material'
 import { makeStyles } from '@masknet/theme'
-import { useI18N } from '../../../utils'
+import { useI18N } from '../locales'
 
 const useStyles = makeStyles()((theme) => {
     return {
@@ -40,20 +40,20 @@ export interface RedpacketMessagePanelProps {
 export function RedpacketMessagePanel(props: RedpacketMessagePanelProps) {
     const { onChange, message } = props
     const { classes } = useStyles()
-    const { t } = useI18N()
+    const t = useI18N()
 
     return (
         <Box className={classes.root}>
             <div className={classes.wrapper}>
                 <Typography className={classes.title} color="textSecondary" variant="body2" component="span">
-                    {t('plugin_red_packet_message_label')}
+                    {t.message_label()}
                 </Typography>
             </div>
             <div className={classNames(classes.wrapper)}>
                 <InputBase
                     className={classes.input}
                     onChange={(e) => onChange(e.target.value)}
-                    inputProps={{ placeholder: t('plugin_red_packet_best_wishes') }}
+                    inputProps={{ placeholder: t.best_wishes() }}
                     value={message}
                 />
             </div>
diff --git a/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedpacketNftConfirmDialog.tsx b/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedpacketNftConfirmDialog.tsx
index 0a676a0f9a01..3b96cc508aee 100644
--- a/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedpacketNftConfirmDialog.tsx
+++ b/packages/mask/src/plugins/RedPacket/SNSAdaptor/RedpacketNftConfirmDialog.tsx
@@ -20,7 +20,7 @@ import { Button, Grid, Link, Typography, DialogContent, List, ListItem } from '@
 import ActionButton from '../../../extension/options-page/DashboardComponents/ActionButton'
 import { EthereumWalletConnectedBoundary } from '../../../web3/UI/EthereumWalletConnectedBoundary'
 import LaunchIcon from '@mui/icons-material/Launch'
-import { useI18N } from '../../../utils'
+import { useI18N as useBaseI18N } from '../../../utils'
 import { useCreateNftRedpacketCallback } from './hooks/useCreateNftRedpacketCallback'
 import { useCurrentIdentity } from '../../../components/DataSource/useActivatedUI'
 import { useCompositionContext } from '@masknet/plugin-infra/content-script'
@@ -29,6 +29,7 @@ import { WalletMessages } from '../../Wallet/messages'
 import { RedPacketRPC } from '../messages'
 import { useAsync } from 'react-use'
 import Services from '../../../extension/service'
+import { useI18N } from '../locales'
 
 const useStyles = makeStyles()((theme) => ({
     root: {
@@ -167,7 +168,8 @@ export function RedpacketNftConfirmDialog(props: RedpacketNftConfirmDialogProps)
     const web3 = useWeb3()
     const { attachMetadata } = useCompositionContext()
 
-    const { t } = useI18N()
+    const { t: i18n } = useBaseI18N()
+    const t = useI18N()
     const { address: publicKey, privateKey } = useMemo(() => web3.eth.accounts.create(), [])
     const duration = 60 * 60 * 24
     const currentIdentity = useCurrentIdentity()
@@ -236,12 +238,12 @@ export function RedpacketNftConfirmDialog(props: RedpacketNftConfirmDialogProps)
     }, [createState, onSendPost])
 
     return (
-        <InjectedDialog open={open} onClose={onBack} title={t('confirm')} maxWidth="xs">
+        <InjectedDialog open={open} onClose={onBack} title={i18n('confirm')} maxWidth="xs">
             <DialogContent className={classes.root}>
                 <Grid container spacing={2}>
                     <Grid item xs={6}>
                         <Typography color="textPrimary" variant="body1" className={classes.text}>
-                            {t('plugin_red_packet_nft_account_name')}
+                            {t.nft_account_name()}
                         </Typography>
                     </Grid>
                     <Grid item xs={6}>
@@ -266,7 +268,7 @@ export function RedpacketNftConfirmDialog(props: RedpacketNftConfirmDialogProps)
                     </Grid>
                     <Grid item xs={6}>
                         <Typography variant="body1" color="textPrimary" className={classNames(classes.text)}>
-                            {t('plugin_red_packet_nft_attached_message')}
+                            {t.nft_attached_message()}
                         </Typography>
                     </Grid>
                     <Grid item xs={6}>
@@ -274,13 +276,13 @@ export function RedpacketNftConfirmDialog(props: RedpacketNftConfirmDialogProps)
                             variant="body1"
                             color="textPrimary"
                             align="right"
-                            className={(classes.text, classes.bold, classes.ellipsis)}>
+                            className={classNames(classes.text, classes.bold, classes.ellipsis)}>
                             {message}
                         </Typography>
                     </Grid>
                     <Grid item xs={6}>
                         <Typography variant="body1" color="textPrimary" className={classNames(classes.text)}>
-                            {t('plugin_wallet_collections')}
+                            {t.collections()}
                         </Typography>
                     </Grid>
                     <Grid item xs={6}>
@@ -307,7 +309,7 @@ export function RedpacketNftConfirmDialog(props: RedpacketNftConfirmDialogProps)
 
                     <Grid item xs={6}>
                         <Typography color="textPrimary" variant="body1" className={classNames(classes.text)}>
-                            {t('plugin_red_packet_nft_total_amount')}
+                            {t.nft_total_amount()}
                         </Typography>
                     </Grid>
                     <Grid item xs={6}>
@@ -327,7 +329,7 @@ export function RedpacketNftConfirmDialog(props: RedpacketNftConfirmDialogProps)
                             onClick={onBack}
                             size="large"
                             variant="contained">
-                            {t('cancel')}
+                            {i18n('cancel')}
                         </Button>
                     </Grid>
                     <Grid item xs={6}>
@@ -344,8 +346,8 @@ export function RedpacketNftConfirmDialog(props: RedpacketNftConfirmDialogProps)
                                 onClick={onSendTx}
                                 className={classNames(classes.button, classes.sendButton)}
                                 fullWidth>
-                                {t('plugin_red_packet_send_symbol', {
-                                    amount: tokenList.length,
+                                {t.send_symbol({
+                                    amount: tokenList.length.toString(),
                                     symbol: tokenList.length > 1 ? 'NFTs' : 'NFT',
                                 })}
                             </ActionButton>
diff --git a/packages/mask/src/plugins/RedPacket/SNSAdaptor/SelectNftTokenDialog.tsx b/packages/mask/src/plugins/RedPacket/SNSAdaptor/SelectNftTokenDialog.tsx
index 0915e71da8a8..acf9198e6695 100644
--- a/packages/mask/src/plugins/RedPacket/SNSAdaptor/SelectNftTokenDialog.tsx
+++ b/packages/mask/src/plugins/RedPacket/SNSAdaptor/SelectNftTokenDialog.tsx
@@ -8,17 +8,17 @@ import {
     isSameAddress,
     formatNFT_TokenId,
 } from '@masknet/web3-shared-evm'
-import { useI18N } from '../../../utils'
+import { useI18N as useBaseI18N } from '../../../utils'
 import { DialogContent, Box, InputBase, Paper, Button, Typography, ListItem, CircularProgress } from '@mui/material'
 import QuestionMarkIcon from '@mui/icons-material/QuestionMark'
 import { makeStyles, ShadowRootTooltip } from '@masknet/theme'
 import { useCallback, useState, useEffect } from 'react'
 import { SearchIcon } from '@masknet/icons'
 import CheckIcon from '@mui/icons-material/Check'
-import { Trans } from 'react-i18next'
 import { useUpdate } from 'react-use'
 import { findLastIndex } from 'lodash-unified'
 import { NFT_RED_PACKET_MAX_SHARES } from '../constants'
+import { useI18N, Translate } from '../locales'
 
 interface StyleProps {
     isSelectSharesExceed: boolean
@@ -320,7 +320,8 @@ export function SelectNftTokenDialog(props: SelectNftTokenDialogProps) {
         onClose,
         loadingOwnerList,
     } = props
-    const { t } = useI18N()
+    const { t: tr } = useBaseI18N()
+    const t = useI18N()
     const account = useAccount()
     const [tokenDetailed, setTokenDetailed] = useState<OrderedERC721Token>()
     const [searched, setSearched] = useState(false)
@@ -435,8 +436,7 @@ export function SelectNftTokenDialog(props: SelectNftTokenDialogProps) {
     }, [tokenDetailed, tokenDetailedSelectedList, setExistTokenDetailedList, onClose])
 
     const NonExistedTokenList = () => (
-        <Trans
-            i18nKey="plugin_red_packet_nft_non_existed_tip"
+        <Translate.nft_non_existed_tip
             components={{
                 tokenIdList: (
                     <span>
@@ -452,12 +452,10 @@ export function SelectNftTokenDialog(props: SelectNftTokenDialogProps) {
         />
     )
 
+    const maxSharesOptions = { amount: NFT_RED_PACKET_MAX_SHARES.toString() }
+
     return (
-        <InjectedDialog
-            open={open}
-            onClose={onClose}
-            title={t('plugin_red_packet_nft_select_collection')}
-            maxWidth="xs">
+        <InjectedDialog open={open} onClose={onClose} title={t.nft_select_collection()} maxWidth="xs">
             {tokenDetailedOwnerList.length === 0 ? (
                 <DialogContent className={classes.dialogContent}>
                     <Box className={classes.tokenBox}>
@@ -476,17 +474,13 @@ export function SelectNftTokenDialog(props: SelectNftTokenDialogProps) {
                                 className={classes.searchButton}
                                 variant="contained"
                                 onClick={onSearch}>
-                                {t('search')}
+                                {t.search()}
                             </Button>
                         </div>
                         {loadingToken || !tokenDetailed || !contract ? (
                             <Box className={classes.noResultBox}>
                                 <Typography>
-                                    {loadingToken
-                                        ? t('wallet_loading_token')
-                                        : searched
-                                        ? t('wallet_search_no_result')
-                                        : null}
+                                    {loadingToken ? t.loading_token() : searched ? t.search_no_result() : null}
                                 </Typography>
                             </Box>
                         ) : (
@@ -512,21 +506,15 @@ export function SelectNftTokenDialog(props: SelectNftTokenDialogProps) {
                     </Box>
                     <div className={classes.selectSharesExceedBox}>
                         <Typography className={classes.selectSharesExceed}>
-                            {isSelectSharesExceed
-                                ? t('plugin_red_packet_nft_max_shares_tip', { amount: NFT_RED_PACKET_MAX_SHARES })
-                                : null}
+                            {isSelectSharesExceed ? t.nft_max_shares_tip(maxSharesOptions) : null}
                         </Typography>
                         <Box className={classes.selectAmountBox}>
                             <ShadowRootTooltip
                                 title={
                                     <Typography className={classes.tooltipText}>
                                         {tokenDetailedSelectedList.length > NFT_RED_PACKET_MAX_SHARES
-                                            ? t('plugin_red_packet_nft_max_shares_tip', {
-                                                  amount: NFT_RED_PACKET_MAX_SHARES,
-                                              })
-                                            : t('plugin_red_packet_nft_max_shares', {
-                                                  amount: NFT_RED_PACKET_MAX_SHARES,
-                                              })}
+                                            ? t.nft_max_shares_tip(maxSharesOptions)
+                                            : t.nft_max_shares(maxSharesOptions)}
                                     </Typography>
                                 }
                                 placement="top-end"
@@ -546,10 +534,10 @@ export function SelectNftTokenDialog(props: SelectNftTokenDialogProps) {
                         variant="contained"
                         onClick={onSubmit}>
                         {tokenDetailed && !isOwner
-                            ? t('wallet_add_nft_invalid_owner')
+                            ? t.nft_invalid_owner()
                             : isAdded
-                            ? t('wallet_add_nft_already_added')
-                            : t('confirm')}
+                            ? t.nft_already_added()
+                            : tr('confirm')}
                     </Button>
                 </DialogContent>
             ) : (
@@ -574,14 +562,12 @@ export function SelectNftTokenDialog(props: SelectNftTokenDialogProps) {
                                 className={classes.searchButton}
                                 variant="contained"
                                 onClick={tokenDetailedOwnerList.length === 0 ? onSearch : onFilter}>
-                                {t('search')}
+                                {t.search()}
                             </Button>
                         </div>
                         {(loadingToken || !tokenDetailed) && searched ? (
                             <Box className={classes.noResultBox}>
-                                <Typography>
-                                    {loadingToken ? t('wallet_loading_token') : t('wallet_search_no_result')}
-                                </Typography>
+                                <Typography>{loadingToken ? t.loading_token() : t.search_no_result()}</Typography>
                             </Box>
                         ) : tokenDetailed?.info.name ? (
                             <Box className={classNames(classes.wrapper, classes.nftWrapper)}>
@@ -606,12 +592,11 @@ export function SelectNftTokenDialog(props: SelectNftTokenDialogProps) {
                                                 {selectAll ? <CheckIcon className={classes.checkIcon} /> : null}
                                             </div>
                                             <Typography className={classNames(classes.selectAllCheckBoxText)}>
-                                                {t('select_all')}
+                                                {tr('select_all')}
                                             </Typography>
                                         </div>
                                         <Typography>
-                                            <Trans
-                                                i18nKey="plugin_red_packet_nft_shift_select_tip"
+                                            <Translate.nft_shift_select_tip
                                                 components={{
                                                     text: <span style={{ color: '#1C68F3' }} />,
                                                 }}
@@ -670,9 +655,7 @@ export function SelectNftTokenDialog(props: SelectNftTokenDialogProps) {
                                 ) : null}
                             </Typography>
                             <Typography className={classes.selectSharesExceed}>
-                                {isSelectSharesExceed
-                                    ? t('plugin_red_packet_nft_max_shares_tip', { amount: NFT_RED_PACKET_MAX_SHARES })
-                                    : null}
+                                {isSelectSharesExceed ? t.nft_max_shares_tip(maxSharesOptions) : null}
                             </Typography>
                         </div>
 
@@ -681,12 +664,8 @@ export function SelectNftTokenDialog(props: SelectNftTokenDialogProps) {
                                 title={
                                     <Typography className={classes.tooltipText}>
                                         {tokenDetailedSelectedList.length > NFT_RED_PACKET_MAX_SHARES
-                                            ? t('plugin_red_packet_nft_max_shares_tip', {
-                                                  amount: NFT_RED_PACKET_MAX_SHARES,
-                                              })
-                                            : t('plugin_red_packet_nft_max_shares', {
-                                                  amount: NFT_RED_PACKET_MAX_SHARES,
-                                              })}
+                                            ? t.nft_max_shares_tip(maxSharesOptions)
+                                            : t.nft_max_shares(maxSharesOptions)}
                                     </Typography>
                                 }
                                 placement="top-end"
@@ -714,10 +693,10 @@ export function SelectNftTokenDialog(props: SelectNftTokenDialogProps) {
                         variant="contained"
                         onClick={onSubmit}>
                         {tokenDetailed && !isOwner
-                            ? t('wallet_add_nft_invalid_owner')
+                            ? t.nft_invalid_owner()
                             : isAdded
-                            ? t('wallet_add_nft_already_added')
-                            : t('confirm')}
+                            ? t.nft_already_added()
+                            : tr('confirm')}
                     </Button>
                 </DialogContent>
             )}
diff --git a/packages/mask/src/plugins/RedPacket/SNSAdaptor/hooks/useCreateCallback.tsx b/packages/mask/src/plugins/RedPacket/SNSAdaptor/hooks/useCreateCallback.tsx
index aced424d9e3e..f5b967e28b4c 100644
--- a/packages/mask/src/plugins/RedPacket/SNSAdaptor/hooks/useCreateCallback.tsx
+++ b/packages/mask/src/plugins/RedPacket/SNSAdaptor/hooks/useCreateCallback.tsx
@@ -5,14 +5,13 @@ import {
     EthereumTokenType,
     FungibleTokenDetailed,
     TransactionEventType,
-    TransactionStateType,
     useAccount,
     useChainId,
     useTokenConstants,
-    useTransactionState,
 } from '@masknet/web3-shared-evm'
 import { omit } from 'lodash-unified'
 import { useCallback } from 'react'
+import { useAsyncFn } from 'react-use'
 import type { TransactionReceipt } from 'web3-core'
 import Web3Utils from 'web3-utils'
 import { useRedPacketContract } from './useRedPacketContract'
@@ -120,17 +119,10 @@ export function useCreateParams(redPacketSettings: RedPacketSettings | undefined
 export function useCreateCallback(redPacketSettings: RedPacketSettings, version: number, publicKey: string) {
     const account = useAccount()
     const chainId = useChainId()
-    const [createState, setCreateState] = useTransactionState()
     const redPacketContract = useRedPacketContract(version)
     const getCreateParams = useCreateParams(redPacketSettings, version, publicKey)
 
-    const resetCallback = useCallback(() => {
-        setCreateState({
-            type: TransactionStateType.UNKNOWN,
-        })
-    }, [])
-    const createCallback = useCallback(async () => {
-        resetCallback()
+    return useAsyncFn(async () => {
         const { token } = redPacketSettings
         const createParams = await getCreateParams()
 
@@ -139,28 +131,15 @@ export function useCreateCallback(redPacketSettings: RedPacketSettings, version:
         const { gas, params, paramsObj, gasError } = createParams
 
         if (gasError) {
-            setCreateState({
-                type: TransactionStateType.FAILED,
-                error: gasError,
-            })
             return
         }
 
         try {
             checkParams(paramsObj)
         } catch (error) {
-            setCreateState({
-                type: TransactionStateType.FAILED,
-                error: error as Error,
-            })
             return
         }
 
-        // pre-step: start waiting for provider to confirm tx
-        setCreateState({
-            type: TransactionStateType.WAIT_FOR_CONFIRMING,
-        })
-
         // estimate gas and compose transaction
         const value = toFixed(token.type === EthereumTokenType.Native ? paramsObj.total : 0)
         const config: PayableTx = {
@@ -175,22 +154,11 @@ export function useCreateCallback(redPacketSettings: RedPacketSettings, version:
                 .create_red_packet(...params)
                 .send(config)
                 .on(TransactionEventType.CONFIRMATION, (no, receipt) => {
-                    setCreateState({
-                        type: TransactionStateType.CONFIRMED,
-                        no,
-                        receipt,
-                    })
                     resolve(receipt)
                 })
                 .on(TransactionEventType.ERROR, (error: Error) => {
-                    setCreateState({
-                        type: TransactionStateType.FAILED,
-                        error,
-                    })
                     reject(error)
                 })
         })
     }, [account, redPacketContract, redPacketSettings, chainId, getCreateParams])
-
-    return [createState, createCallback, resetCallback] as const
 }
diff --git a/packages/mask/src/plugins/RedPacket/SNSAdaptor/index.tsx b/packages/mask/src/plugins/RedPacket/SNSAdaptor/index.tsx
index f56ca1eb9aec..d1ec432b53a0 100644
--- a/packages/mask/src/plugins/RedPacket/SNSAdaptor/index.tsx
+++ b/packages/mask/src/plugins/RedPacket/SNSAdaptor/index.tsx
@@ -1,4 +1,4 @@
-import { type Plugin, usePluginWrapper } from '@masknet/plugin-infra/content-script'
+import { type Plugin, usePluginWrapper, PluginId } from '@masknet/plugin-infra/content-script'
 import {
     ChainId,
     EthereumTokenType,
@@ -94,9 +94,9 @@ const sns: Plugin.SNSAdaptor.Definition = {
     ApplicationEntries: [
         (() => {
             const icon = <RedPacketIcon />
-            const name = <Trans i18nKey="plugin_red_packet_name" />
+            const name = <Trans ns={PluginId.RedPacket} i18nKey="name" />
             const recommendFeature = {
-                description: <Trans i18nKey="plugin_red_packet_recommend_feature_description" />,
+                description: <Trans ns={PluginId.RedPacket} i18nKey="recommend_feature_description" />,
                 backgroundGradient: 'linear-gradient(180.54deg, #FF9A9E 0.71%, #FECFEF 98.79%, #FECFEF 99.78%)',
             }
             return {
@@ -125,7 +125,7 @@ const sns: Plugin.SNSAdaptor.Definition = {
                 appBoardSortingDefaultPriority: 1,
                 marketListSortingPriority: 1,
                 icon,
-                description: <Trans i18nKey="plugin_red_packet_description" />,
+                description: <Trans ns={PluginId.RedPacket} i18nKey="description" />,
                 name,
                 tutorialLink: 'https://realmasknetwork.notion.site/0a71fd421aae4563bd07caa3e2129e5b',
                 category: 'dapp',
diff --git a/packages/mask/src/plugins/RedPacket/base.ts b/packages/mask/src/plugins/RedPacket/base.ts
index cb9aeeea4ca9..c1fe7aa7fc8b 100644
--- a/packages/mask/src/plugins/RedPacket/base.ts
+++ b/packages/mask/src/plugins/RedPacket/base.ts
@@ -2,6 +2,7 @@ import type { Plugin } from '@masknet/plugin-infra'
 import { NetworkPluginID } from '@masknet/plugin-infra/web3'
 import { ChainId } from '@masknet/web3-shared-evm'
 import { RedPacketMetaKey, RedPacketNftMetaKey, RedPacketPluginID } from './constants'
+import { languages } from './locales/languages'
 
 export const base: Plugin.Shared.Definition = {
     ID: RedPacketPluginID,
@@ -40,4 +41,5 @@ export const base: Plugin.Shared.Definition = {
     contribution: {
         metadataKeys: new Set([RedPacketMetaKey, RedPacketNftMetaKey]),
     },
+    i18n: languages,
 }
diff --git a/packages/mask/src/plugins/RedPacket/locales/en-US.json b/packages/mask/src/plugins/RedPacket/locales/en-US.json
new file mode 100644
index 000000000000..b3491141c7f6
--- /dev/null
+++ b/packages/mask/src/plugins/RedPacket/locales/en-US.json
@@ -0,0 +1,84 @@
+{
+    "promote": "🧧🧧🧧 Try sending Lucky Drop to your friends with tokens or NFTs to share the joy now! Install Mask.io to send your first Lucky Drop.",
+    "promote_short": "🧧🧧🧧 Try sending Lucky Drop to your friends with Mask.io.",
+    "nft_shift_select_tip": "You can also use <text>{{text}}</text> to select multiple NFTs.",
+    "collections": "Collections",
+    "select_a_token": "Select a Token",
+    "search": "Search",
+    "loading_token": "Loading token...",
+    "search_no_result": "No results.",
+    "nft_already_added": "The collectible has already been added.",
+    "nft_invalid_owner": "The collectible does not belong to you.",
+    "nft_max_shares": "The maximum number of NFTs to be sold in NFT lucky drop contract is {{amount}}.",
+    "nft_max_shares_tip": "The NFT lucky drop supports up to {{amount, number}} NFTs selected for one time.",
+    "nft_non_existed_tip": "Token ID <tokenIdList></tokenIdList> does not exist or belong to you.",
+    "nft_select_collection": "Choose your collection",
+    "completed": "Completed",
+    "expired": "Expired",
+    "nft_tip": "This is an NFT lucky drop.",
+    "nft_share_foreshow_message_not_twitter": "@{{sender}} is sending an NFT lucky drop on {{network}} network. \n{{payload}}",
+    "nft_share_foreshow_message": "@{{sender}} is sending an NFT lucky drop on {{network}} network. Follow @{{account}} (mask.io) to claim NFT lucky drops.\n#mask_io #LuckyDrop\n{{payload}}",
+    "nft_share_claimed_message_not_twitter": "I just claimed an NFT lucky drop from @{{sender}} on {{network}} network.\n$t(promote_short) \n{{payload}}",
+    "nft_share_claimed_message": "I just claimed an NFT lucky drop from @{{sender}} on {{network}} network. Follow @{{account}} (mask.io) to claim NFT lucky drops.\n$t(promote_short)\n#mask_io #LuckyDrop\n{{payload}}",
+    "nft_total_amount": "Total Amount",
+    "nft_attached_message": "Attached Message",
+    "nft_account_name": "Wallet account",
+    "message_label": "Title",
+    "claiming": "Claiming...",
+    "claim": "Claim",
+    "data_broken": "The Lucky Drop can’t be sent due to data damage. Please withdraw the assets after {{duration}}.",
+    "refund": "Refund",
+    "empty": "Empty",
+    "refunding": "Refunding",
+    "history_send": "Send",
+    "history_total_amount": "Total Amount: {{amount}} {{symbol}}",
+    "history_split_mode": "Split Mode: {{mode}}",
+    "history_total_claimed_amount": "Total: <span><strong>{{claimedAmount}}/{{amount}}</strong></span> {{symbol}}",
+    "attached_message": "Attached Message",
+    "indivisible": "The minimum amount for each share is {{amount}} {{symbol}}",
+    "name": "Lucky Drop",
+    "description": "Gift crypto or NFTs to any users, first come, first served.",
+    "next": "Next",
+    "nft_approve_all_tip": "Note: When selecting approve all, all NFTs in the contract will be authorized for sale by default, including the NFTs transfered later.",
+    "nft_unapproved_tip": "You can get the rest of your NFTs back after 24 hours of sending.",
+    "nft_select_partially_option": "Select partially",
+    "nft_select_all_option": "ALL ({{total}} NFT)",
+    "erc721_insufficient_balance": "Insufficient Balance",
+    "erc721_tab_title": "Collectibles",
+    "erc20_tab_title": "Token",
+    "claimed": "Claimed",
+    "details": "Lucky Drop Details",
+    "display_name": "Plugin: Lucky Drop",
+    "select_existing": "Past",
+    "create_new": "New",
+    "send_symbol": "Send {{amount}} {{symbol}}",
+    "back": "Back",
+    "hint": "You can withdraw the remaining balance 24 hours after the Lucky Drop is sent.",
+    "amount_total": "Amount Total",
+    "amount_per_share": "Amount per Share",
+    "shares": "Shares",
+    "average": "Average",
+    "random": "Random",
+    "split_mode": "Split Mode",
+    "token": "Token",
+    "chain_not_supported": "Not supported on {{chain}} yet.",
+    "nft_no_history": "You haven’t created any NFT lucky drop yet. Try to create and share lucky with your friends.",
+    "history_claimed": "Claimed: <strong>{{claimedShares}}/{{shares}}</strong> Share",
+    "send": "Send",
+    "history_duration": "Time: {{startTime}} ~ {{endTime}} (UTC+8)",
+    "best_wishes": "Best Wishes!",
+    "nft_data_broken": "The Lucky Drop can’t be sent due to data damage.",
+    "from": "From: @{{name}}",
+    "share_message_official_account": "I just claimed a lucky drop from @{{sender}} on {{network}} network. Follow @{{account}} (mask.io) to claim lucky drops.\n$t(promote_short)\n#mask_io #LuckyDrop\n{{payload}}",
+    "share_message_not_twitter": "I just claimed a lucky drop from @{{sender}} on {{network}} network.\n$t(promote_short)\n{{payload}}",
+    "share_unclaimed_message_official_account": "Hi friends, I just found a lucky drop sent by @{{sender}} on {{network}} network. Follow @{{account}} (mask.io) to claim lucky drops.\n$t(promote_short)\n#mask_io #LuckyDrop\n{{payload}}",
+    "share_unclaimed_message_not_twitter": "Hi friends, I just found a lucky drop sent by @{{sender}} on {{network}} network.\n$t(promote_short)\n{{payload}}",
+    "description_claimed": "You got {{amount}} {{symbol}}",
+    "description_refund": "You could refund {{balance}} {{symbol}}.",
+    "description_refunded": "The Lucky Drop has been refunded.",
+    "description_expired": "The Lucky Drop is expired.",
+    "description_broken": "The Lucky Drop is broken.",
+    "description_empty": "The Lucky Drop is empty.",
+    "description_failover": "{{shares}} shares / {{total}} {{symbol}}",
+    "recommend_feature_description": "Surpirse your social friends with Lucky Drop"
+}
diff --git a/packages/mask/src/plugins/RedPacket/locales/index.ts b/packages/mask/src/plugins/RedPacket/locales/index.ts
new file mode 100644
index 000000000000..d6ead60252e4
--- /dev/null
+++ b/packages/mask/src/plugins/RedPacket/locales/index.ts
@@ -0,0 +1,6 @@
+// This file is auto generated. DO NOT EDIT
+// Run `npx gulp sync-languages` to regenerate.
+// Default fallback language in a family of languages are chosen by the alphabet order
+// To overwrite this, please overwrite packages/scripts/src/locale-kit-next/index.ts
+
+export * from './i18n_generated'
diff --git a/packages/mask/src/plugins/RedPacket/locales/ja-JP.json b/packages/mask/src/plugins/RedPacket/locales/ja-JP.json
new file mode 100644
index 000000000000..0967ef424bce
--- /dev/null
+++ b/packages/mask/src/plugins/RedPacket/locales/ja-JP.json
@@ -0,0 +1 @@
+{}
diff --git a/packages/mask/src/plugins/RedPacket/locales/ko-KR.json b/packages/mask/src/plugins/RedPacket/locales/ko-KR.json
new file mode 100644
index 000000000000..0967ef424bce
--- /dev/null
+++ b/packages/mask/src/plugins/RedPacket/locales/ko-KR.json
@@ -0,0 +1 @@
+{}
diff --git a/packages/mask/src/plugins/RedPacket/locales/languages.ts b/packages/mask/src/plugins/RedPacket/locales/languages.ts
new file mode 100644
index 000000000000..e8cd5a8b1cd5
--- /dev/null
+++ b/packages/mask/src/plugins/RedPacket/locales/languages.ts
@@ -0,0 +1,34 @@
+// This file is auto generated. DO NOT EDIT
+// Run `npx gulp sync-languages` to regenerate.
+// Default fallback language in a family of languages are chosen by the alphabet order
+// To overwrite this, please overwrite packages/scripts/src/locale-kit-next/index.ts
+import en_US from './en-US.json'
+import ja_JP from './ja-JP.json'
+import ko_KR from './ko-KR.json'
+import qya_AA from './qya-AA.json'
+import zh_CN from './zh-CN.json'
+import zh_TW from './zh-TW.json'
+export const languages = {
+    en: en_US,
+    ja: ja_JP,
+    ko: ko_KR,
+    qy: qya_AA,
+    'zh-CN': zh_CN,
+    zh: zh_TW,
+}
+// @ts-ignore
+if (import.meta.webpackHot) {
+    // @ts-ignore
+    import.meta.webpackHot.accept(
+        ['./en-US.json', './ja-JP.json', './ko-KR.json', './qya-AA.json', './zh-CN.json', './zh-TW.json'],
+        () =>
+            globalThis.dispatchEvent?.(
+                new CustomEvent('MASK_I18N_HMR', {
+                    detail: [
+                        'com.maskbook.red_packet',
+                        { en: en_US, ja: ja_JP, ko: ko_KR, qy: qya_AA, 'zh-CN': zh_CN, zh: zh_TW },
+                    ],
+                }),
+            ),
+    )
+}
diff --git a/packages/mask/src/plugins/RedPacket/locales/qya-AA.json b/packages/mask/src/plugins/RedPacket/locales/qya-AA.json
new file mode 100644
index 000000000000..0967ef424bce
--- /dev/null
+++ b/packages/mask/src/plugins/RedPacket/locales/qya-AA.json
@@ -0,0 +1 @@
+{}
diff --git a/packages/mask/src/plugins/RedPacket/locales/zh-CN.json b/packages/mask/src/plugins/RedPacket/locales/zh-CN.json
new file mode 100644
index 000000000000..0967ef424bce
--- /dev/null
+++ b/packages/mask/src/plugins/RedPacket/locales/zh-CN.json
@@ -0,0 +1 @@
+{}
diff --git a/packages/mask/src/plugins/RedPacket/locales/zh-TW.json b/packages/mask/src/plugins/RedPacket/locales/zh-TW.json
new file mode 100644
index 000000000000..0967ef424bce
--- /dev/null
+++ b/packages/mask/src/plugins/RedPacket/locales/zh-TW.json
@@ -0,0 +1 @@
+{}
diff --git a/packages/mask/src/plugins/Savings/SNSAdaptor/SavingsForm.tsx b/packages/mask/src/plugins/Savings/SNSAdaptor/SavingsForm.tsx
index 7986af9309d3..47d5a9576b6c 100644
--- a/packages/mask/src/plugins/Savings/SNSAdaptor/SavingsForm.tsx
+++ b/packages/mask/src/plugins/Savings/SNSAdaptor/SavingsForm.tsx
@@ -13,6 +13,7 @@ import {
     formatCurrency,
     getAaveConstants,
     isSameAddress,
+    resolveChainName,
     useAccount,
     useFungibleTokenBalance,
     useTokenConstants,
@@ -26,7 +27,6 @@ import { useAsync, useAsyncFn } from 'react-use'
 import type { AbiItem } from 'web3-utils'
 import { ActionButtonPromise } from '../../../extension/options-page/DashboardComponents/ActionButton'
 import { activatedSocialNetworkUI } from '../../../social-network'
-import { isFacebook } from '../../../social-network-adaptor/facebook.com/base'
 import { isTwitter } from '../../../social-network-adaptor/twitter.com/base'
 import { useI18N } from '../../../utils'
 import { EthereumChainBoundary } from '../../../web3/UI/EthereumChainBoundary'
@@ -164,16 +164,12 @@ export function SavingsForm({ chainId, protocol, tab, onClose }: SavingsFormProp
     }, [protocol.bareToken, inputAmount, chainId])
 
     const openShareTxDialog = useOpenShareTxDialog()
-    const shareText = [
-        `I just deposit ${inputAmount} ${protocol.bareToken.symbol} with ${resolveProtocolName(protocol.type)}. ${
-            isTwitter(activatedSocialNetworkUI) || isFacebook(activatedSocialNetworkUI)
-                ? `Follow @${
-                      isTwitter(activatedSocialNetworkUI) ? t('twitter_account') : t('facebook_account')
-                  } (mask.io) to deposit.`
-                : ''
-        }`,
-        '#mask_io',
-    ].join('\n')
+    const shareText = t('promote_savings', {
+        amount: inputAmount,
+        symbol: protocol.bareToken.symbol,
+        chain: resolveChainName(chainId),
+        account: isTwitter(activatedSocialNetworkUI) ? t('twitter_account') : t('facebook_account'),
+    })
     const [, executor] = useAsyncFn(async () => {
         switch (tab) {
             case TabType.Deposit:
diff --git a/packages/mask/src/plugins/Snapshot/SNSAdaptor/VotingCard.tsx b/packages/mask/src/plugins/Snapshot/SNSAdaptor/VotingCard.tsx
index 1d09bbab169f..068d235a77f1 100644
--- a/packages/mask/src/plugins/Snapshot/SNSAdaptor/VotingCard.tsx
+++ b/packages/mask/src/plugins/Snapshot/SNSAdaptor/VotingCard.tsx
@@ -13,6 +13,7 @@ import { usePower } from './hooks/usePower'
 import { VoteConfirmDialog } from './VoteConfirmDialog'
 import { useRetry } from './hooks/useRetry'
 import { NetworkPluginID, useCurrentWeb3NetworkPluginID } from '@masknet/plugin-infra/web3'
+import { activatedSocialNetworkUI } from '../../../social-network'
 
 const useStyles = makeStyles()((theme) => {
     return {
@@ -52,11 +53,12 @@ export function VotingCard() {
     const networkPluginId = useCurrentWeb3NetworkPluginID()
     const retry = useRetry()
     const onVoteConfirm = useSnackbarCallback(
-        () => {
+        async () => {
             setLoading(true)
-            return PluginSnapshotRPC.vote(identifier, choice, account, proposal.type)
+            await PluginSnapshotRPC.vote(identifier, choice, account, proposal.type)
+            activatedSocialNetworkUI.utils.share?.(t('promote_snapshot'))
         },
-        [choice, identifier],
+        [choice, identifier, account, proposal.type, t],
         () => {
             setLoading(false)
             setOpen(false)
diff --git a/packages/mask/src/plugins/Trader/SNSAdaptor/trader/Trader.tsx b/packages/mask/src/plugins/Trader/SNSAdaptor/trader/Trader.tsx
index 0f3f1536c37f..656592bc4d28 100644
--- a/packages/mask/src/plugins/Trader/SNSAdaptor/trader/Trader.tsx
+++ b/packages/mask/src/plugins/Trader/SNSAdaptor/trader/Trader.tsx
@@ -21,7 +21,8 @@ import { useUnmount, useUpdateEffect } from 'react-use'
 import { activatedSocialNetworkUI } from '../../../../social-network'
 import { isFacebook } from '../../../../social-network-adaptor/facebook.com/base'
 import { isTwitter } from '../../../../social-network-adaptor/twitter.com/base'
-import { useI18N } from '../../../../utils'
+import { useI18N as useBaseI18N } from '../../../../utils'
+import { useI18N } from '../../locales'
 import { isNativeTokenWrapper } from '../../helpers'
 import { PluginTraderMessages } from '../../messages'
 import { AllProviderTradeActionType, AllProviderTradeContext } from '../../trader/useAllProviderTradeContext'
@@ -61,7 +62,8 @@ export function Trader(props: TraderProps) {
     const chainIdValid = useChainIdValid()
     const { NATIVE_TOKEN_ADDRESS } = useTokenConstants()
     const classes = useStylesExtends(useStyles(), props)
-    const { t } = useI18N()
+    const { t: tr } = useBaseI18N()
+    const t = useI18N()
     const { setTargetChainId } = TargetChainIdContext.useContainer()
 
     // #region trade state
@@ -248,16 +250,17 @@ export function Trader(props: TraderProps) {
                   }.${
                       isTwitter(activatedSocialNetworkUI) || isFacebook(activatedSocialNetworkUI)
                           ? `Follow @${
-                                isTwitter(activatedSocialNetworkUI) ? t('twitter_account') : t('facebook_account')
+                                isTwitter(activatedSocialNetworkUI) ? tr('twitter_account') : tr('facebook_account')
                             } (mask.io) to swap cryptocurrencies on ${
                                 isTwitter(activatedSocialNetworkUI) ? 'Twitter' : 'Facebook'
                             }.`
                           : ''
                   }`,
                   '#mask_io',
+                  t.promote(),
               ].join('\n')
             : ''
-    }, [focusedTrade?.value, inputToken, outputToken])
+    }, [focusedTrade?.value, inputToken, outputToken, tr, t])
     const openShareTxDialog = useOpenShareTxDialog()
     const onConfirmDialogConfirm = useCallback(async () => {
         setOpenConfirmDialog(false)
diff --git a/packages/mask/src/plugins/Trader/base.ts b/packages/mask/src/plugins/Trader/base.ts
index 2bb1969f0c29..e9970b26ff6b 100644
--- a/packages/mask/src/plugins/Trader/base.ts
+++ b/packages/mask/src/plugins/Trader/base.ts
@@ -2,6 +2,7 @@ import type { Plugin } from '@masknet/plugin-infra'
 import { NetworkPluginID } from '@masknet/plugin-infra/web3'
 import { ChainId } from '@masknet/web3-shared-evm'
 import { PLUGIN_ID } from './constants'
+import { languages } from './locales/languages'
 
 export const base: Plugin.Shared.Definition = {
     ID: PLUGIN_ID,
@@ -30,4 +31,5 @@ export const base: Plugin.Shared.Definition = {
             [NetworkPluginID.PLUGIN_SOLANA]: { supportedChainIds: [] },
         },
     },
+    i18n: languages,
 }
diff --git a/packages/mask/src/plugins/Trader/locales/en-US.json b/packages/mask/src/plugins/Trader/locales/en-US.json
new file mode 100644
index 000000000000..5920a7a664f4
--- /dev/null
+++ b/packages/mask/src/plugins/Trader/locales/en-US.json
@@ -0,0 +1,3 @@
+{
+    "promote": "Instantly token exchange on Twitter. Install Mask.io to explore “better server” on Multi-chain Dex."
+}
diff --git a/packages/mask/src/plugins/Trader/locales/index.ts b/packages/mask/src/plugins/Trader/locales/index.ts
new file mode 100644
index 000000000000..d6ead60252e4
--- /dev/null
+++ b/packages/mask/src/plugins/Trader/locales/index.ts
@@ -0,0 +1,6 @@
+// This file is auto generated. DO NOT EDIT
+// Run `npx gulp sync-languages` to regenerate.
+// Default fallback language in a family of languages are chosen by the alphabet order
+// To overwrite this, please overwrite packages/scripts/src/locale-kit-next/index.ts
+
+export * from './i18n_generated'
diff --git a/packages/mask/src/plugins/Trader/locales/ja-JP.json b/packages/mask/src/plugins/Trader/locales/ja-JP.json
new file mode 100644
index 000000000000..0967ef424bce
--- /dev/null
+++ b/packages/mask/src/plugins/Trader/locales/ja-JP.json
@@ -0,0 +1 @@
+{}
diff --git a/packages/mask/src/plugins/Trader/locales/ko-KR.json b/packages/mask/src/plugins/Trader/locales/ko-KR.json
new file mode 100644
index 000000000000..0967ef424bce
--- /dev/null
+++ b/packages/mask/src/plugins/Trader/locales/ko-KR.json
@@ -0,0 +1 @@
+{}
diff --git a/packages/mask/src/plugins/Trader/locales/languages.ts b/packages/mask/src/plugins/Trader/locales/languages.ts
new file mode 100644
index 000000000000..a189384a2e40
--- /dev/null
+++ b/packages/mask/src/plugins/Trader/locales/languages.ts
@@ -0,0 +1,34 @@
+// This file is auto generated. DO NOT EDIT
+// Run `npx gulp sync-languages` to regenerate.
+// Default fallback language in a family of languages are chosen by the alphabet order
+// To overwrite this, please overwrite packages/scripts/src/locale-kit-next/index.ts
+import en_US from './en-US.json'
+import ja_JP from './ja-JP.json'
+import ko_KR from './ko-KR.json'
+import qya_AA from './qya-AA.json'
+import zh_CN from './zh-CN.json'
+import zh_TW from './zh-TW.json'
+export const languages = {
+    en: en_US,
+    ja: ja_JP,
+    ko: ko_KR,
+    qy: qya_AA,
+    'zh-CN': zh_CN,
+    zh: zh_TW,
+}
+// @ts-ignore
+if (import.meta.webpackHot) {
+    // @ts-ignore
+    import.meta.webpackHot.accept(
+        ['./en-US.json', './ja-JP.json', './ko-KR.json', './qya-AA.json', './zh-CN.json', './zh-TW.json'],
+        () =>
+            globalThis.dispatchEvent?.(
+                new CustomEvent('MASK_I18N_HMR', {
+                    detail: [
+                        'com.maskbook.trader',
+                        { en: en_US, ja: ja_JP, ko: ko_KR, qy: qya_AA, 'zh-CN': zh_CN, zh: zh_TW },
+                    ],
+                }),
+            ),
+    )
+}
diff --git a/packages/mask/src/plugins/Trader/locales/qya-AA.json b/packages/mask/src/plugins/Trader/locales/qya-AA.json
new file mode 100644
index 000000000000..0967ef424bce
--- /dev/null
+++ b/packages/mask/src/plugins/Trader/locales/qya-AA.json
@@ -0,0 +1 @@
+{}
diff --git a/packages/mask/src/plugins/Trader/locales/zh-CN.json b/packages/mask/src/plugins/Trader/locales/zh-CN.json
new file mode 100644
index 000000000000..0967ef424bce
--- /dev/null
+++ b/packages/mask/src/plugins/Trader/locales/zh-CN.json
@@ -0,0 +1 @@
+{}
diff --git a/packages/mask/src/plugins/Trader/locales/zh-TW.json b/packages/mask/src/plugins/Trader/locales/zh-TW.json
new file mode 100644
index 000000000000..0967ef424bce
--- /dev/null
+++ b/packages/mask/src/plugins/Trader/locales/zh-TW.json
@@ -0,0 +1 @@
+{}
diff --git a/packages/mask/src/plugins/dHEDGE/UI/InvestDialog.tsx b/packages/mask/src/plugins/dHEDGE/UI/InvestDialog.tsx
index 53f8e5eda38c..d636468993f7 100644
--- a/packages/mask/src/plugins/dHEDGE/UI/InvestDialog.tsx
+++ b/packages/mask/src/plugins/dHEDGE/UI/InvestDialog.tsx
@@ -94,7 +94,7 @@ export function InvestDialog() {
     // #endregion
 
     // #region blocking
-    const [isInvesting, investCallback] = useInvestCallback(pool, amount.toFixed(), token)
+    const [{ loading: isInvesting }, investCallback] = useInvestCallback(pool, amount.toFixed(), token)
     const openShareTxDialog = useOpenShareTxDialog()
     const cashTag = isTwitter(activatedSocialNetworkUI) ? '$' : ''
     const shareText = token
diff --git a/packages/mask/src/plugins/dHEDGE/hooks/useInvestCallback.ts b/packages/mask/src/plugins/dHEDGE/hooks/useInvestCallback.ts
index ced58ff42cde..ec087c129d64 100644
--- a/packages/mask/src/plugins/dHEDGE/hooks/useInvestCallback.ts
+++ b/packages/mask/src/plugins/dHEDGE/hooks/useInvestCallback.ts
@@ -1,6 +1,6 @@
 import { toFixed } from '@masknet/web3-shared-base'
 import { EthereumTokenType, FungibleTokenDetailed, TransactionEventType, useAccount } from '@masknet/web3-shared-evm'
-import { useCallback, useState } from 'react'
+import { useAsyncFn } from 'react-use'
 import { useDHedgePoolV1Contract, useDHedgePoolV2Contract } from '../contracts/useDHedgePool'
 import { Pool, PoolType } from '../types'
 
@@ -15,9 +15,8 @@ export function useInvestCallback(pool: Pool | undefined, amount: string, token?
     const poolV2Contract = useDHedgePoolV2Contract(pool?.address ?? '')
 
     const account = useAccount()
-    const [loading, setLoading] = useState(false)
 
-    const investCallback = useCallback(async () => {
+    return useAsyncFn(async () => {
         if (!token || !poolV1Contract || !poolV2Contract) return
 
         // step 1: estimate gas
@@ -32,11 +31,9 @@ export function useInvestCallback(pool: Pool | undefined, amount: string, token?
                 : poolV2Contract.methods.deposit(token.address, amount)
         }
 
-        setLoading(true)
         const estimatedGas = await deposit()
             .estimateGas(config)
             .catch((error) => {
-                setLoading(false)
                 throw error
             })
 
@@ -53,6 +50,4 @@ export function useInvestCallback(pool: Pool | undefined, amount: string, token?
                 .on(TransactionEventType.ERROR, reject)
         })
     }, [pool, account, amount, token])
-
-    return [loading, investCallback] as const
 }
diff --git a/packages/mask/src/social-network-adaptor/twitter.com/injection/NFT/NFTAvatarInTwitter.tsx b/packages/mask/src/social-network-adaptor/twitter.com/injection/NFT/NFTAvatarInTwitter.tsx
index 1fb279a56651..b8a0682dcea7 100644
--- a/packages/mask/src/social-network-adaptor/twitter.com/injection/NFT/NFTAvatarInTwitter.tsx
+++ b/packages/mask/src/social-network-adaptor/twitter.com/injection/NFT/NFTAvatarInTwitter.tsx
@@ -1,4 +1,4 @@
-import { createReactRootShadowed, MaskMessages, NFTAvatarEvent, startWatch } from '../../../../utils'
+import { createReactRootShadowed, MaskMessages, NFTAvatarEvent, startWatch, useI18N } from '../../../../utils'
 import { searchTwitterAvatarLinkSelector, searchTwitterAvatarSelector } from '../../utils/selector'
 import { MutationObserverWatcher } from '@dimensiondev/holoflows-kit'
 import { makeStyles } from '@masknet/theme'
@@ -16,6 +16,9 @@ import { trim } from 'lodash-unified'
 import { RSS3_KEY_SNS } from '../../../../plugins/Avatar/constants'
 import { openWindow } from '@masknet/shared-base-ui'
 import { usePersonaNFTAvatar } from '../../../../plugins/Avatar/hooks/usePersonaNFTAvatar'
+import { NFTCardStyledAssetPlayer, useShowConfirm } from '@masknet/shared'
+import { Box, Typography } from '@mui/material'
+import { activatedSocialNetworkUI } from '../../../../social-network'
 
 export function injectNFTAvatarInTwitter(signal: AbortSignal) {
     const watcher = new MutationObserverWatcher(searchTwitterAvatarSelector())
@@ -61,6 +64,7 @@ function NFTAvatarInTwitter() {
     const windowSize = useWindowSize()
     const location = useLocation()
 
+    const { t } = useI18N()
     const showAvatar = useMemo(
         () => getAvatarId(identity.avatar ?? '') === avatar?.avatarId && avatar.avatarId,
         [avatar?.avatarId, identity.avatar],
@@ -81,6 +85,7 @@ function NFTAvatarInTwitter() {
     const onUpdate = (data: NFTAvatarEvent) => {
         setNFTEvent(data)
     }
+    const openConfirmDialog = useShowConfirm()
 
     // After the avatar is set, it cannot be saved immediately, and must wait until the avatar of twitter is updated
     useAsync(async () => {
@@ -118,6 +123,21 @@ function NFTAvatarInTwitter() {
             window.alert('Sorry, failed to save NFT Avatar. Please set again.')
             return
         }
+        openConfirmDialog({
+            title: t('plugin_avatar_setup_share_title'),
+            content: (
+                <Box display="flex" flexDirection="column" alignItems="center">
+                    <NFTCardStyledAssetPlayer contractAddress={avatar.address} tokenId={avatar.tokenId} />
+                    <Typography mt={3} fontSize="18px">
+                        {t('plugin_avatar_setup_success')}
+                    </Typography>
+                </Box>
+            ),
+            confirmLabel: t('share'),
+            onSubmit() {
+                activatedSocialNetworkUI.utils.share?.(t('plugin_avatar_setup_pfp_share'))
+            },
+        })
 
         setAvatar(avatar)
         MaskMessages.events.NFTAvatarTimelineUpdated.sendToAll(
@@ -130,7 +150,7 @@ function NFTAvatarInTwitter() {
         )
 
         setNFTEvent(undefined)
-    }, [identity.avatar])
+    }, [identity.avatar, openConfirmDialog, t])
 
     useEffect(() => {
         setAvatar(_avatar)
diff --git a/packages/mask/src/tsconfig.json b/packages/mask/src/tsconfig.json
index 9d137580ffed..5be5ef7a1573 100644
--- a/packages/mask/src/tsconfig.json
+++ b/packages/mask/src/tsconfig.json
@@ -31,6 +31,7 @@
     { "path": "../../plugin-infra" },
     { "path": "../../plugins/Wallet" },
     { "path": "../../plugins/Flow" },
+    { "path": "../../plugins/FileService" },
     { "path": "../../external-plugin-previewer" },
     { "path": "../../injected-script/sdk" },
     { "path": "../../mask-sdk/server" },
diff --git a/packages/mask/src/web3/UI/EthereumERC20TokenApprovedBoundary.tsx b/packages/mask/src/web3/UI/EthereumERC20TokenApprovedBoundary.tsx
index 8759226ec53d..7ae770d3fc76 100644
--- a/packages/mask/src/web3/UI/EthereumERC20TokenApprovedBoundary.tsx
+++ b/packages/mask/src/web3/UI/EthereumERC20TokenApprovedBoundary.tsx
@@ -154,7 +154,7 @@ export function EthereumERC20TokenApprovedBoundary(props: EthereumERC20TokenAppr
                 ) : null}
             </Box>
         )
-    if (approveStateType === ApproveStateType.PENDING || approveStateType === ApproveStateType.UPDATING)
+    if (transactionState.loading || approveStateType === ApproveStateType.UPDATING)
         return (
             <Grid container>
                 <ActionButton
@@ -164,7 +164,7 @@ export function EthereumERC20TokenApprovedBoundary(props: EthereumERC20TokenAppr
                     size="large"
                     disabled
                     {...props.ActionButtonProps}>
-                    {approveStateType === ApproveStateType.PENDING
+                    {transactionState.loading
                         ? t('plugin_ito_unlocking_symbol', { symbol: token.symbol })
                         : `Updating ${token.symbol}`}
                     &hellip;
diff --git a/packages/mask/src/web3/UI/EthereumERC721TokenApprovedBoundary.tsx b/packages/mask/src/web3/UI/EthereumERC721TokenApprovedBoundary.tsx
index e10bd3c2760b..e84035bd6bf9 100644
--- a/packages/mask/src/web3/UI/EthereumERC721TokenApprovedBoundary.tsx
+++ b/packages/mask/src/web3/UI/EthereumERC721TokenApprovedBoundary.tsx
@@ -1,17 +1,16 @@
+import { makeStyles, useCustomSnackbar, useStylesExtends } from '@masknet/theme'
 import {
-    useERC721ContractIsApproveForAll,
-    useERC721ContractSetApproveForAllCallback,
-    TransactionStateType,
     ERC721ContractDetailed,
     resolveTransactionLinkOnExplorer,
+    useERC721ContractIsApproveForAll,
+    useERC721ContractSetApproveForAllCallback,
 } from '@masknet/web3-shared-evm'
 import OpenInNewIcon from '@mui/icons-material/OpenInNew'
-import { useI18N } from '../../utils'
-import { makeStyles, useCustomSnackbar, useStylesExtends } from '@masknet/theme'
-import { Typography, Link } from '@mui/material'
-import ActionButton, { ActionButtonProps } from '../../extension/options-page/DashboardComponents/ActionButton'
-import { useMemo, useEffect } from 'react'
+import { Link, Typography } from '@mui/material'
+import { useCallback, useMemo } from 'react'
 import { EthereumAddress } from 'wallet.ts'
+import ActionButton, { ActionButtonProps } from '../../extension/options-page/DashboardComponents/ActionButton'
+import { useI18N } from '../../utils'
 
 const useStyles = makeStyles()(() => ({
     snackBarText: {
@@ -48,45 +47,39 @@ export function EthereumERC721TokenApprovedBoundary(props: EthereumERC712TokenAp
     const { t } = useI18N()
     const classes = useStylesExtends(useStyles(), props)
     const { value, loading, retry } = useERC721ContractIsApproveForAll(contractDetailed?.address, owner, operator)
-    const [approveState, approveCallback, resetCallback] = useERC721ContractSetApproveForAllCallback(
+    const [approveState, approveCallback] = useERC721ContractSetApproveForAllCallback(
         contractDetailed?.address,
         operator,
         true,
     )
     const { showSnackbar } = useCustomSnackbar()
 
-    useEffect(() => {
-        if (approveState.type === TransactionStateType.CONFIRMED && approveState.no === 0) {
-            showSnackbar(
-                <div className={classes.snackBar}>
-                    <Typography className={classes.snackBarText}>
-                        {t('plugin_wallet_approve_all_nft_successfully', { symbol: contractDetailed?.symbol })}
-                    </Typography>
-                    <Link
-                        href={resolveTransactionLinkOnExplorer(
-                            contractDetailed!.chainId,
-                            approveState.receipt.transactionHash,
-                        )}
-                        target="_blank"
-                        rel="noopener noreferrer"
-                        className={classes.snackBarLink}>
-                        <OpenInNewIcon className={classes.openIcon} />
-                    </Link>
-                </div>,
-                {
-                    variant: 'success',
-                    anchorOrigin: { horizontal: 'right', vertical: 'top' },
-                },
-            )
-            resetCallback()
-            retry()
-        } else if (approveState.type === TransactionStateType.FAILED) {
-            showSnackbar(approveState.error.message, {
-                variant: 'error',
-            })
-            resetCallback()
+    const approve = useCallback(async () => {
+        const hash = await approveCallback()
+        if (typeof hash !== 'string') {
+            showSnackbar(approveState.error?.message)
+            return
         }
-    }, [approveState, contractDetailed])
+        showSnackbar(
+            <div className={classes.snackBar}>
+                <Typography className={classes.snackBarText}>
+                    {t('plugin_wallet_approve_all_nft_successfully', { symbol: contractDetailed?.symbol })}
+                </Typography>
+                <Link
+                    href={resolveTransactionLinkOnExplorer(contractDetailed!.chainId, hash)}
+                    target="_blank"
+                    rel="noopener noreferrer"
+                    className={classes.snackBarLink}>
+                    <OpenInNewIcon className={classes.openIcon} />
+                </Link>
+            </div>,
+            {
+                variant: 'success',
+                anchorOrigin: { horizontal: 'right', vertical: 'top' },
+            },
+        )
+        retry()
+    }, [showSnackbar, approveState, retry, contractDetailed])
 
     const validationMessage = useMemo(() => {
         if (!contractDetailed?.address || !EthereumAddress.isValid(contractDetailed?.address))
@@ -97,7 +90,7 @@ export function EthereumERC721TokenApprovedBoundary(props: EthereumERC712TokenAp
         return ''
     }, [contractDetailed, owner, operator, _validationMessage])
 
-    if ([TransactionStateType.WAIT_FOR_CONFIRMING, TransactionStateType.HASH].includes(approveState.type)) {
+    if (approveState.loading) {
         return (
             <ActionButton
                 className={classes.approveButton}
@@ -147,7 +140,7 @@ export function EthereumERC721TokenApprovedBoundary(props: EthereumERC712TokenAp
                 variant="contained"
                 size="large"
                 fullWidth
-                onClick={approveCallback}
+                onClick={approve}
                 {...props.ActionButtonProps}>
                 {t('plugin_wallet_approve_all_nft', {
                     symbol: contractDetailed?.symbol
diff --git a/packages/plugins/FileService/src/index.ts b/packages/plugins/FileService/src/index.ts
index 69226b786ae4..79ee5520eaa9 100644
--- a/packages/plugins/FileService/src/index.ts
+++ b/packages/plugins/FileService/src/index.ts
@@ -1,6 +1,8 @@
 import { registerPlugin } from '@masknet/plugin-infra'
 import { base } from './base'
 
+export { FileInfoMetadataReader } from './helpers'
+
 registerPlugin({
     ...base,
     SNSAdaptor: {
diff --git a/packages/web3-shared/evm/hooks/useERC20TokenApproveCallback.ts b/packages/web3-shared/evm/hooks/useERC20TokenApproveCallback.ts
index 346b49771765..2e52b2049043 100644
--- a/packages/web3-shared/evm/hooks/useERC20TokenApproveCallback.ts
+++ b/packages/web3-shared/evm/hooks/useERC20TokenApproveCallback.ts
@@ -1,13 +1,13 @@
-import { useCallback, useMemo } from 'react'
-import { once } from 'lodash-unified'
 import type { NonPayableTx } from '@masknet/web3-contracts/types/types'
-import { TransactionStateType, TransactionEventType } from '../types'
+import { isLessThan, toFixed } from '@masknet/web3-shared-base'
+import { once } from 'lodash-unified'
+import { useCallback, useMemo } from 'react'
+import { useAsyncFn } from 'react-use'
 import { useERC20TokenContract } from '../contracts/useERC20TokenContract'
+import { TransactionEventType } from '../types'
 import { useAccount } from './useAccount'
 import { useERC20TokenAllowance } from './useERC20TokenAllowance'
 import { useERC20TokenBalance } from './useERC20TokenBalance'
-import { useTransactionState } from './useTransactionState'
-import { isLessThan, toFixed } from '@masknet/web3-shared-base'
 
 const MaxUint256 = toFixed('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff')
 
@@ -23,7 +23,6 @@ export enum ApproveStateType {
 export function useERC20TokenApproveCallback(address?: string, amount?: string, spender?: string) {
     const account = useAccount()
     const erc20Contract = useERC20TokenContract(address)
-    const [transactionState, setTransactionState] = useTransactionState()
 
     // read the approved information from the chain
     const {
@@ -44,43 +43,20 @@ export function useERC20TokenApproveCallback(address?: string, amount?: string,
         if (!amount || !spender) return ApproveStateType.UNKNOWN
         if (loadingBalance || loadingAllowance) return ApproveStateType.UPDATING
         if (errorBalance || errorAllowance) return ApproveStateType.FAILED
-        if (transactionState.type === TransactionStateType.WAIT_FOR_CONFIRMING) return ApproveStateType.PENDING
         return isLessThan(allowance, amount) ? ApproveStateType.NOT_APPROVED : ApproveStateType.APPROVED
-    }, [
-        amount,
-        spender,
-        balance,
-        allowance,
-        errorBalance,
-        errorAllowance,
-        loadingAllowance,
-        loadingBalance,
-        transactionState.type,
-    ])
+    }, [amount, spender, balance, allowance, errorBalance, errorAllowance, loadingAllowance, loadingBalance])
 
-    const approveCallback = useCallback(
+    const [state, approveCallback] = useAsyncFn(
         async (useExact = false) => {
-            setTransactionState({
-                type: TransactionStateType.UNKNOWN,
-            })
             if (approveStateType === ApproveStateType.UNKNOWN || !amount || !spender || !erc20Contract) {
                 return
             }
 
             // error: failed to approve token
             if (approveStateType !== ApproveStateType.NOT_APPROVED) {
-                setTransactionState({
-                    type: TransactionStateType.FAILED,
-                    error: new Error('Failed to approve token.'),
-                })
                 return
             }
 
-            // start waiting for provider to confirm tx
-            setTransactionState({
-                type: TransactionStateType.WAIT_FOR_CONFIRMING,
-            })
-
             // estimate gas and compose transaction
             const config = {
                 from: account,
@@ -96,16 +72,12 @@ export function useERC20TokenApproveCallback(address?: string, amount?: string,
                         })
                     })
                     .catch((error) => {
-                        setTransactionState({
-                            type: TransactionStateType.FAILED,
-                            error,
-                        })
                         throw error
                     }),
             }
 
             // send transaction and wait for hash
-            return new Promise<void>(async (resolve, reject) => {
+            return new Promise<string>(async (resolve, reject) => {
                 const revalidate = once(() => {
                     revalidateBalance()
                     revalidateAllowance()
@@ -113,43 +85,25 @@ export function useERC20TokenApproveCallback(address?: string, amount?: string,
                 erc20Contract.methods
                     .approve(spender, useExact ? amount : MaxUint256)
                     .send(config as NonPayableTx)
-                    .on(TransactionEventType.RECEIPT, (receipt) => {
-                        setTransactionState({
-                            type: TransactionStateType.CONFIRMED,
-                            no: 0,
-                            receipt,
-                        })
+                    .on(TransactionEventType.RECEIPT, () => {
                         revalidate()
-                        resolve()
                     })
                     .on(TransactionEventType.CONFIRMATION, (no, receipt) => {
-                        setTransactionState({
-                            type: TransactionStateType.CONFIRMED,
-                            no,
-                            receipt,
-                        })
                         revalidate()
-                        resolve()
+                        resolve(receipt.transactionHash)
                     })
                     .on(TransactionEventType.ERROR, (error) => {
-                        setTransactionState({
-                            type: TransactionStateType.FAILED,
-                            error,
-                        })
                         revalidate()
                         reject(error)
                     })
             })
         },
-        [account, amount, balance, spender, loadingAllowance, loadingBalance, erc20Contract, approveStateType],
+        [account, amount, spender, erc20Contract, approveStateType],
     )
 
     const resetCallback = useCallback(() => {
         revalidateBalance()
         revalidateAllowance()
-        setTransactionState({
-            type: TransactionStateType.UNKNOWN,
-        })
     }, [revalidateBalance, revalidateAllowance])
 
     return [
@@ -160,7 +114,7 @@ export function useERC20TokenApproveCallback(address?: string, amount?: string,
             spender,
             balance,
         },
-        transactionState,
+        state,
         approveCallback,
         resetCallback,
     ] as const
diff --git a/packages/web3-shared/evm/hooks/useERC721ContractSetApproveForAllCallback.ts b/packages/web3-shared/evm/hooks/useERC721ContractSetApproveForAllCallback.ts
index 6eb6672de354..99c2cf200196 100644
--- a/packages/web3-shared/evm/hooks/useERC721ContractSetApproveForAllCallback.ts
+++ b/packages/web3-shared/evm/hooks/useERC721ContractSetApproveForAllCallback.ts
@@ -1,9 +1,8 @@
-import { useCallback } from 'react'
 import type { NonPayableTx } from '@masknet/web3-contracts/types/types'
-import { useAccount } from './useAccount'
-import { useTransactionState } from './useTransactionState'
+import { useAsyncFn } from 'react-use'
 import { useERC721TokenContract } from '../contracts'
-import { TransactionStateType, TransactionEventType } from '../types'
+import { TransactionEventType } from '../types'
+import { useAccount } from './useAccount'
 import { useChainId } from './useChainId'
 
 /**
@@ -19,62 +18,32 @@ export function useERC721ContractSetApproveForAllCallback(
     const account = useAccount()
     const chainId = useChainId()
     const erc721TokenContract = useERC721TokenContract(contractAddress)
-    const [approveState, setApproveState] = useTransactionState()
 
-    const approveCallback = useCallback(async () => {
+    return useAsyncFn(async () => {
         if (!erc721TokenContract || !contractAddress || !operator) {
-            setApproveState({ type: TransactionStateType.UNKNOWN })
             return
         }
 
-        setApproveState({
-            type: TransactionStateType.WAIT_FOR_CONFIRMING,
-        })
-
         const config = {
             from: account,
             gas: await erc721TokenContract.methods
                 .setApprovalForAll(operator, approved)
                 .estimateGas({ from: account })
                 .catch((error) => {
-                    setApproveState({ type: TransactionStateType.FAILED, error })
                     throw error
                 }),
         }
 
-        return new Promise<void>(async (resolve, reject) => {
+        return new Promise<string>(async (resolve, reject) => {
             erc721TokenContract.methods
                 .setApprovalForAll(operator, approved)
                 .send(config as NonPayableTx)
-                .on(TransactionEventType.RECEIPT, (receipt) => {
-                    setApproveState({
-                        type: TransactionStateType.CONFIRMED,
-                        no: 0,
-                        receipt,
-                    })
-                    resolve()
-                })
                 .on(TransactionEventType.CONFIRMATION, (no, receipt) => {
-                    setApproveState({
-                        type: TransactionStateType.CONFIRMED,
-                        no,
-                        receipt,
-                    })
-                    resolve()
+                    resolve(receipt.transactionHash)
                 })
                 .on(TransactionEventType.ERROR, (error) => {
-                    setApproveState({
-                        type: TransactionStateType.FAILED,
-                        error,
-                    })
                     reject(error)
                 })
         })
-    }, [account, chainId, erc721TokenContract, approved, contractAddress, operator, setApproveState])
-
-    const resetCallback = useCallback(() => {
-        setApproveState({ type: TransactionStateType.UNKNOWN })
-    }, [])
-
-    return [approveState, approveCallback, resetCallback] as const
+    }, [account, chainId, erc721TokenContract, approved, contractAddress, operator])
 }
diff --git a/packages/web3-shared/evm/hooks/useTransactionCallback.ts b/packages/web3-shared/evm/hooks/useTransactionCallback.ts
index b2a8f6f446d2..a2f6d92d0f7e 100644
--- a/packages/web3-shared/evm/hooks/useTransactionCallback.ts
+++ b/packages/web3-shared/evm/hooks/useTransactionCallback.ts
@@ -1,32 +1,24 @@
 import type { PayableTransactionObject, PayableTx } from '@masknet/web3-contracts/types/types'
 import { omit } from 'lodash-unified'
-import { useCallback, useState } from 'react'
+import { useAsyncFn } from 'react-use'
 import { TransactionEventType } from '../types'
 
 export function useTransactionCallback<T extends unknown>(
     config: PayableTx | undefined,
     method: PayableTransactionObject<T> | undefined,
 ) {
-    const [loading, setLoading] = useState(false)
-
-    const updateCallback = useCallback(async () => {
+    return useAsyncFn(async () => {
         if (!config || !method) return
 
         const gasExpectedConfig = { ...config }
 
-        setLoading(true)
         try {
             const estimatedGas = await method.estimateGas(omit(config, 'gas'))
             if (!gasExpectedConfig.gas && estimatedGas) {
                 gasExpectedConfig.gas = estimatedGas
             }
         } catch (error) {
-            try {
-                await method.call(config)
-            } catch (error) {
-                setLoading(false)
-                throw error
-            }
+            await method.call(config)
         }
 
         return new Promise<string>(async (resolve, reject) => {
@@ -40,6 +32,4 @@ export function useTransactionCallback<T extends unknown>(
                 })
         })
     }, [config, method])
-
-    return [loading, updateCallback] as const
 }

From ee01b6dba5f16c1ce0f226daff3c3566de03cc73 Mon Sep 17 00:00:00 2001
From: Mariia <msynoverska@webinerds.com>
Date: Mon, 13 Jun 2022 14:30:39 +0100
Subject: [PATCH 4/5] feat: add tokensListAttrace to token list

---
 .../shared-ui/FarmTokenDetailed.tsx           |  7 ++-
 .../shared-ui/RewardFarmPostWidget.tsx        |  2 +-
 .../SNSAdaptor/shared-ui/TokenDetailed.tsx    |  4 +-
 .../plugins/Referral/Worker/apis/discovery.ts | 28 ++++++++++-
 .../Referral/Worker/apis/tokenLists.ts        | 46 +++++++++++++++++--
 5 files changed, 77 insertions(+), 10 deletions(-)

diff --git a/packages/mask/src/plugins/Referral/SNSAdaptor/shared-ui/FarmTokenDetailed.tsx b/packages/mask/src/plugins/Referral/SNSAdaptor/shared-ui/FarmTokenDetailed.tsx
index 7f4c930068b6..67720d434ed5 100644
--- a/packages/mask/src/plugins/Referral/SNSAdaptor/shared-ui/FarmTokenDetailed.tsx
+++ b/packages/mask/src/plugins/Referral/SNSAdaptor/shared-ui/FarmTokenDetailed.tsx
@@ -39,6 +39,7 @@ export interface TokenProps {
     symbol?: string
     name?: string
     logoURI?: string
+    logoURL?: string
     chainId?: ChainId
 }
 
@@ -55,7 +56,11 @@ export function FarmTokenDetailed({ token, hideFarmTypeIcon = false }: FarmToken
         <Box display="flex" alignItems="center">
             {token && (
                 <>
-                    {token ? <TokenIcon {...token} /> : <div className={classes.tokenIcon} />}
+                    {token ? (
+                        <TokenIcon {...token} logoURL={token.logoURL || token.logoURI} />
+                    ) : (
+                        <div className={classes.tokenIcon} />
+                    )}
                     <Box display="flex" flexDirection="column" className={classes.tokenDetails}>
                         <Typography display="flex" alignItems="center" className={classes.farmName} fontWeight="500">
                             {token.symbol} {t.referral_farm()} {!hideFarmTypeIcon && <SponsoredFarmIcon />}
diff --git a/packages/mask/src/plugins/Referral/SNSAdaptor/shared-ui/RewardFarmPostWidget.tsx b/packages/mask/src/plugins/Referral/SNSAdaptor/shared-ui/RewardFarmPostWidget.tsx
index 6fd465a1c3b0..90146c7c6a04 100644
--- a/packages/mask/src/plugins/Referral/SNSAdaptor/shared-ui/RewardFarmPostWidget.tsx
+++ b/packages/mask/src/plugins/Referral/SNSAdaptor/shared-ui/RewardFarmPostWidget.tsx
@@ -23,7 +23,7 @@ export function RewardFarmPostWidget({ title, icon, rewardData, tokenSymbol }: R
     const { classes } = useStyles()
 
     return (
-        <Grid item xs={6} marginTop="24px">
+        <Grid item xs={12} marginTop="24px">
             {title && (
                 <Grid item xs={12} container marginBottom="12px" alignItems="center">
                     {icon && icon}
diff --git a/packages/mask/src/plugins/Referral/SNSAdaptor/shared-ui/TokenDetailed.tsx b/packages/mask/src/plugins/Referral/SNSAdaptor/shared-ui/TokenDetailed.tsx
index 4e24bb2088f2..ab74af33a6d4 100644
--- a/packages/mask/src/plugins/Referral/SNSAdaptor/shared-ui/TokenDetailed.tsx
+++ b/packages/mask/src/plugins/Referral/SNSAdaptor/shared-ui/TokenDetailed.tsx
@@ -30,6 +30,7 @@ export interface TokenProps {
     symbol?: string
     name?: string
     logoURI?: string
+    logoURL?: string
     chainId?: ChainId
 }
 
@@ -39,12 +40,11 @@ export interface TokenDetailedProps extends React.PropsWithChildren<{}> {
 
 export function TokenDetailed({ token }: TokenDetailedProps) {
     const { classes } = useStyles()
-
     return (
         <div className={classes.container}>
             {token && (
                 <>
-                    <TokenIcon {...token} />
+                    <TokenIcon {...token} logoURL={token.logoURL || token.logoURI} />
                     <Box className={classes.details} display="flex" flexDirection="column">
                         <Typography>{token.symbol}</Typography>
                         <span className={classes.name}>{token.name}</span>
diff --git a/packages/mask/src/plugins/Referral/Worker/apis/discovery.ts b/packages/mask/src/plugins/Referral/Worker/apis/discovery.ts
index 50f50565adae..c724b6c07a9b 100644
--- a/packages/mask/src/plugins/Referral/Worker/apis/discovery.ts
+++ b/packages/mask/src/plugins/Referral/Worker/apis/discovery.ts
@@ -1,11 +1,15 @@
+import type { ChainId } from '@masknet/web3-shared-evm'
+
 import type { Discovery, DiscoveryFarmOracles } from '../../types'
 import { supportedChainId } from '../../constants'
 
+const URL = 'https://discovery.attrace.com'
+
 export async function getFullDiscovery(): Promise<{
     discovery: Discovery
     pop: string
 }> {
-    const response = await fetch('https://discovery.attrace.com/mainnet/full.json')
+    const response = await fetch(`${URL}/mainnet/full.json`)
 
     const discovery = await response.json()
     const pop = response.headers.get('x-amz-cf-pop') || ''
@@ -20,7 +24,7 @@ export async function getFarmOraclesDiscovery(): Promise<{
     pop: string
 }> {
     try {
-        const response = await fetch('https://discovery.attrace.com/mainnet/farmOracles.json')
+        const response = await fetch(`${URL}/mainnet/farmOracles.json`)
 
         const discovery = await response.json()
         const pop = response.headers.get('x-amz-cf-pop') || ''
@@ -44,3 +48,23 @@ export async function getReferralFarmsV1Address(): Promise<string> {
         else throw new Error('Failed to fetch Referral farms address.')
     }
 }
+
+export async function fetchAttraceTokenList(): Promise<
+    Array<{
+        chainId: ChainId
+        address: string
+        symbol: string
+        name: string
+        decimals: number
+        logoURI: string
+    }>
+> {
+    try {
+        const response = await fetch(`${URL}/mainnet/tokens.json`)
+
+        const discovery = await response.json()
+        return discovery?.tokens
+    } catch (error) {
+        throw new Error(error as any)
+    }
+}
diff --git a/packages/mask/src/plugins/Referral/Worker/apis/tokenLists.ts b/packages/mask/src/plugins/Referral/Worker/apis/tokenLists.ts
index 7ef4f576471e..f82b99cadefd 100644
--- a/packages/mask/src/plugins/Referral/Worker/apis/tokenLists.ts
+++ b/packages/mask/src/plugins/Referral/Worker/apis/tokenLists.ts
@@ -1,9 +1,47 @@
 import { TokenList } from '@masknet/web3-providers'
-import { type ChainId, getTokenListConstants } from '@masknet/web3-shared-evm'
+import { TokenType } from '@masknet/web3-shared-base'
+import { type ChainId, getTokenListConstants, SchemaType } from '@masknet/web3-shared-evm'
 
-export const fetchERC20TokensFromTokenListsMap = async (chainId: ChainId) => {
+import type { FungibleTokenDetailed } from '../../types'
+
+import { fetchAttraceTokenList } from './discovery'
+
+export const attraceTokenListMapper = async (): Promise<FungibleTokenDetailed[]> => {
+    const tokenList = await fetchAttraceTokenList()
+
+    return tokenList.map((token) => ({
+        id: token.address,
+        type: TokenType.Fungible,
+        schema: SchemaType.ERC20,
+        ...token,
+        logoURL: token.logoURI,
+    }))
+}
+
+// export const fetchERC20TokensFromTokenListsMap = async (
+//     chainId: ChainId,
+// ): Promise<Map<string, FungibleTokenDetailed>> => {
+//     const { FUNGIBLE_TOKEN_LISTS = [] } = getTokenListConstants(chainId)
+
+//     const allListResponse = await Promise.allSettled([
+//         TokenList.fetchFungibleTokensFromTokenLists(chainId, FUNGIBLE_TOKEN_LISTS),
+//         attraceTokenListMapper(),
+//     ])
+//     const [tokensList, tokensListAttrace] = allListResponse.map((x) => (x.status === 'fulfilled' ? x.value : []))
+
+//     return new Map([...tokensList, ...tokensListAttrace].map((token) => [token.address.toLowerCase(), token]))
+// }
+
+export const fetchERC20TokensFromTokenListsMap = async (
+    chainId: ChainId,
+): Promise<Map<string, FungibleTokenDetailed>> => {
     const { FUNGIBLE_TOKEN_LISTS = [] } = getTokenListConstants(chainId)
-    const ERC20Tokens = await TokenList.fetchFungibleTokensFromTokenLists(chainId, FUNGIBLE_TOKEN_LISTS)
 
-    return new Map(ERC20Tokens.map((token) => [token.address.toLowerCase(), token]))
+    const allListResponse = await Promise.allSettled([
+        TokenList.fetchFungibleTokensFromTokenLists(chainId, FUNGIBLE_TOKEN_LISTS),
+        attraceTokenListMapper(),
+    ])
+    const [tokensList, tokensListAttrace] = allListResponse.map((x) => (x.status === 'fulfilled' ? x.value : []))
+
+    return new Map([...tokensList, ...tokensListAttrace].map((token) => [token.address.toLowerCase(), token]))
 }

From 89753a564254dfcb2278513cfb05c0d6f96d992c Mon Sep 17 00:00:00 2001
From: Mariia <msynoverska@webinerds.com>
Date: Mon, 13 Jun 2022 14:54:37 +0100
Subject: [PATCH 5/5] refactor: fetchERC20TokensFromTokenListsMap

---
 .../shared-ui/FarmTokenDetailed.tsx           |  7 +--
 .../SNSAdaptor/shared-ui/TokenDetailed.tsx    |  3 +-
 .../plugins/Referral/Worker/apis/discovery.ts | 28 +----------
 .../Referral/Worker/apis/tokenLists.ts        | 47 +++----------------
 .../mask/src/plugins/Referral/constants.ts    |  2 +
 5 files changed, 13 insertions(+), 74 deletions(-)

diff --git a/packages/mask/src/plugins/Referral/SNSAdaptor/shared-ui/FarmTokenDetailed.tsx b/packages/mask/src/plugins/Referral/SNSAdaptor/shared-ui/FarmTokenDetailed.tsx
index 67720d434ed5..45e0e127e386 100644
--- a/packages/mask/src/plugins/Referral/SNSAdaptor/shared-ui/FarmTokenDetailed.tsx
+++ b/packages/mask/src/plugins/Referral/SNSAdaptor/shared-ui/FarmTokenDetailed.tsx
@@ -38,7 +38,6 @@ export interface TokenProps {
     address: string
     symbol?: string
     name?: string
-    logoURI?: string
     logoURL?: string
     chainId?: ChainId
 }
@@ -56,11 +55,7 @@ export function FarmTokenDetailed({ token, hideFarmTypeIcon = false }: FarmToken
         <Box display="flex" alignItems="center">
             {token && (
                 <>
-                    {token ? (
-                        <TokenIcon {...token} logoURL={token.logoURL || token.logoURI} />
-                    ) : (
-                        <div className={classes.tokenIcon} />
-                    )}
+                    {token ? <TokenIcon {...token} /> : <div className={classes.tokenIcon} />}
                     <Box display="flex" flexDirection="column" className={classes.tokenDetails}>
                         <Typography display="flex" alignItems="center" className={classes.farmName} fontWeight="500">
                             {token.symbol} {t.referral_farm()} {!hideFarmTypeIcon && <SponsoredFarmIcon />}
diff --git a/packages/mask/src/plugins/Referral/SNSAdaptor/shared-ui/TokenDetailed.tsx b/packages/mask/src/plugins/Referral/SNSAdaptor/shared-ui/TokenDetailed.tsx
index ab74af33a6d4..4a76a61772b3 100644
--- a/packages/mask/src/plugins/Referral/SNSAdaptor/shared-ui/TokenDetailed.tsx
+++ b/packages/mask/src/plugins/Referral/SNSAdaptor/shared-ui/TokenDetailed.tsx
@@ -29,7 +29,6 @@ export interface TokenProps {
     address: string
     symbol?: string
     name?: string
-    logoURI?: string
     logoURL?: string
     chainId?: ChainId
 }
@@ -44,7 +43,7 @@ export function TokenDetailed({ token }: TokenDetailedProps) {
         <div className={classes.container}>
             {token && (
                 <>
-                    <TokenIcon {...token} logoURL={token.logoURL || token.logoURI} />
+                    <TokenIcon {...token} />
                     <Box className={classes.details} display="flex" flexDirection="column">
                         <Typography>{token.symbol}</Typography>
                         <span className={classes.name}>{token.name}</span>
diff --git a/packages/mask/src/plugins/Referral/Worker/apis/discovery.ts b/packages/mask/src/plugins/Referral/Worker/apis/discovery.ts
index c724b6c07a9b..50f50565adae 100644
--- a/packages/mask/src/plugins/Referral/Worker/apis/discovery.ts
+++ b/packages/mask/src/plugins/Referral/Worker/apis/discovery.ts
@@ -1,15 +1,11 @@
-import type { ChainId } from '@masknet/web3-shared-evm'
-
 import type { Discovery, DiscoveryFarmOracles } from '../../types'
 import { supportedChainId } from '../../constants'
 
-const URL = 'https://discovery.attrace.com'
-
 export async function getFullDiscovery(): Promise<{
     discovery: Discovery
     pop: string
 }> {
-    const response = await fetch(`${URL}/mainnet/full.json`)
+    const response = await fetch('https://discovery.attrace.com/mainnet/full.json')
 
     const discovery = await response.json()
     const pop = response.headers.get('x-amz-cf-pop') || ''
@@ -24,7 +20,7 @@ export async function getFarmOraclesDiscovery(): Promise<{
     pop: string
 }> {
     try {
-        const response = await fetch(`${URL}/mainnet/farmOracles.json`)
+        const response = await fetch('https://discovery.attrace.com/mainnet/farmOracles.json')
 
         const discovery = await response.json()
         const pop = response.headers.get('x-amz-cf-pop') || ''
@@ -48,23 +44,3 @@ export async function getReferralFarmsV1Address(): Promise<string> {
         else throw new Error('Failed to fetch Referral farms address.')
     }
 }
-
-export async function fetchAttraceTokenList(): Promise<
-    Array<{
-        chainId: ChainId
-        address: string
-        symbol: string
-        name: string
-        decimals: number
-        logoURI: string
-    }>
-> {
-    try {
-        const response = await fetch(`${URL}/mainnet/tokens.json`)
-
-        const discovery = await response.json()
-        return discovery?.tokens
-    } catch (error) {
-        throw new Error(error as any)
-    }
-}
diff --git a/packages/mask/src/plugins/Referral/Worker/apis/tokenLists.ts b/packages/mask/src/plugins/Referral/Worker/apis/tokenLists.ts
index f82b99cadefd..3ffdc0885415 100644
--- a/packages/mask/src/plugins/Referral/Worker/apis/tokenLists.ts
+++ b/packages/mask/src/plugins/Referral/Worker/apis/tokenLists.ts
@@ -1,47 +1,14 @@
 import { TokenList } from '@masknet/web3-providers'
-import { TokenType } from '@masknet/web3-shared-base'
-import { type ChainId, getTokenListConstants, SchemaType } from '@masknet/web3-shared-evm'
+import { type ChainId, getTokenListConstants } from '@masknet/web3-shared-evm'
 
-import type { FungibleTokenDetailed } from '../../types'
+import { TOKEN_LIST_URL } from '../../constants'
 
-import { fetchAttraceTokenList } from './discovery'
-
-export const attraceTokenListMapper = async (): Promise<FungibleTokenDetailed[]> => {
-    const tokenList = await fetchAttraceTokenList()
-
-    return tokenList.map((token) => ({
-        id: token.address,
-        type: TokenType.Fungible,
-        schema: SchemaType.ERC20,
-        ...token,
-        logoURL: token.logoURI,
-    }))
-}
-
-// export const fetchERC20TokensFromTokenListsMap = async (
-//     chainId: ChainId,
-// ): Promise<Map<string, FungibleTokenDetailed>> => {
-//     const { FUNGIBLE_TOKEN_LISTS = [] } = getTokenListConstants(chainId)
-
-//     const allListResponse = await Promise.allSettled([
-//         TokenList.fetchFungibleTokensFromTokenLists(chainId, FUNGIBLE_TOKEN_LISTS),
-//         attraceTokenListMapper(),
-//     ])
-//     const [tokensList, tokensListAttrace] = allListResponse.map((x) => (x.status === 'fulfilled' ? x.value : []))
-
-//     return new Map([...tokensList, ...tokensListAttrace].map((token) => [token.address.toLowerCase(), token]))
-// }
-
-export const fetchERC20TokensFromTokenListsMap = async (
-    chainId: ChainId,
-): Promise<Map<string, FungibleTokenDetailed>> => {
+export const fetchERC20TokensFromTokenListsMap = async (chainId: ChainId) => {
     const { FUNGIBLE_TOKEN_LISTS = [] } = getTokenListConstants(chainId)
-
-    const allListResponse = await Promise.allSettled([
-        TokenList.fetchFungibleTokensFromTokenLists(chainId, FUNGIBLE_TOKEN_LISTS),
-        attraceTokenListMapper(),
+    const ERC20Tokens = await TokenList.fetchFungibleTokensFromTokenLists(chainId, [
+        ...FUNGIBLE_TOKEN_LISTS,
+        TOKEN_LIST_URL,
     ])
-    const [tokensList, tokensListAttrace] = allListResponse.map((x) => (x.status === 'fulfilled' ? x.value : []))
 
-    return new Map([...tokensList, ...tokensListAttrace].map((token) => [token.address.toLowerCase(), token]))
+    return new Map(ERC20Tokens.map((token) => [token.address.toLowerCase(), token]))
 }
diff --git a/packages/mask/src/plugins/Referral/constants.ts b/packages/mask/src/plugins/Referral/constants.ts
index 42fd51e35e3a..52284eaed4e1 100644
--- a/packages/mask/src/plugins/Referral/constants.ts
+++ b/packages/mask/src/plugins/Referral/constants.ts
@@ -21,6 +21,8 @@ export const supportedChainId = ChainId.Mainnet
 export const SWAP_CHAIN_ID = ChainId.Mainnet
 export const supportedOracleChainId = ORACLE_CHAIN_ID.mainnet
 
+export const TOKEN_LIST_URL = 'https://discovery.attrace.com/mainnet/tokens.json'
+
 // testnet example
 // export const supportedChainId = ChainId.Rinkeby
 // export const SWAP_CHAIN_ID = ChainId.Mainnet
