diff --git a/.env b/.env
index 0157db51e..8322cdda4 100644
--- a/.env
+++ b/.env
@@ -1 +1,3 @@
SASS_PATH=node_modules:src/styles
+INLINE_RUNTIME_CHUNK=false
+REACT_APP_SANDBOX=true
diff --git a/package.json b/package.json
index 880ad0854..8293be222 100644
--- a/package.json
+++ b/package.json
@@ -8,7 +8,7 @@
"scripts": {
"start": "react-scripts start",
"build-scripts": "webpack --config scripts/webpack.config.js",
- "build": "cross-env REACT_APP_SANDBOX=true INLINE_RUNTIME_CHUNK=false react-app-rewired build && npm run build-scripts",
+ "build": "cross-env react-app-rewired build && npm run build-scripts",
"test": "react-scripts test",
"prepare": "husky install",
"pre-commit": "lint-staged",
diff --git a/public/manifest.json b/public/manifest.json
index ae2de8ba6..cd5677399 100644
--- a/public/manifest.json
+++ b/public/manifest.json
@@ -1,7 +1,7 @@
{
"manifest_version": 2,
"name": "Terra Station Wallet",
- "version": "2.0.0",
+ "version": "2.1.0",
"background": {
"scripts": ["background.js"],
"persistent": true
diff --git a/src/data/settings/Theme.ts b/src/data/settings/Theme.ts
index 91d458e60..78116b62f 100644
--- a/src/data/settings/Theme.ts
+++ b/src/data/settings/Theme.ts
@@ -30,6 +30,11 @@ export const useThemeFavicon = () => {
return favicon
}
+export const useThemeFront = () => {
+ const { front } = useTheme()
+ return front
+}
+
export const useThemeAnimation = () => {
const { animation } = useTheme()
return animation
diff --git a/src/extension/App.tsx b/src/extension/App.tsx
index 1940ed9ba..6cab8cde4 100644
--- a/src/extension/App.tsx
+++ b/src/extension/App.tsx
@@ -8,6 +8,8 @@ import LatestTx from "app/sections/LatestTx"
import NetworkName from "app/sections/NetworkName"
import SendTx from "txs/send/SendTx"
import SwapTx from "txs/swap/SwapTx"
+import SignMultisigTxPage from "pages/multisig/SignMultisigTxPage"
+import PostMultisigTxPage from "pages/multisig/PostMultisigTxPage"
import { storeNetwork, storeWalletAddress } from "./storage"
import RequestContainer from "./RequestContainer"
import ManageNetworks from "./networks/ManageNetworks"
@@ -33,9 +35,17 @@ const App = () => {
const routes = useRoutes([
{ path: "/networks", element: },
{ path: "/network/new", element: },
+
+ /* auth */
{ path: "/auth/*", element: },
+
+ /* default txs */
{ path: "/send", element: },
{ path: "/swap", element: },
+ { path: "/multisig/sign", element: },
+ { path: "/multisig/post", element: },
+
+ /* 404 */
{ path: "*", element: },
])
diff --git a/src/extension/RequestContainer.tsx b/src/extension/RequestContainer.tsx
index 89c28ff6e..e9c72c983 100644
--- a/src/extension/RequestContainer.tsx
+++ b/src/extension/RequestContainer.tsx
@@ -22,6 +22,7 @@ interface RequestContext {
response: TxResponse,
password?: string
) => void
+ multisigTx: (request: PrimitiveDefaultRequest) => void
}
}
@@ -117,9 +118,26 @@ const RequestContainer: FC = ({ children }) => {
})
}
+ /* multisig */
+ const handleMultisigTx = (request: PrimitiveDefaultRequest) => {
+ // Delete request
+ extension.storage?.local.get(["post"], (storage: ExtensionStorage) => {
+ const list = storage.post || []
+ const next = list.filter(
+ ({ id, origin }) => !(id === request.id && origin === request.origin)
+ )
+
+ extension.storage?.local.set({ post: next }, () => setTx(undefined))
+ })
+ }
+
/* context */
const requests = { connect, tx }
- const actions = { connect: handleConnect, tx: handleTx }
+ const actions = {
+ connect: handleConnect,
+ tx: handleTx,
+ multisigTx: handleMultisigTx,
+ }
return (
{children}
diff --git a/src/extension/auth/Auth.tsx b/src/extension/auth/Auth.tsx
index 226d1a57f..475962047 100644
--- a/src/extension/auth/Auth.tsx
+++ b/src/extension/auth/Auth.tsx
@@ -7,6 +7,7 @@ import AccessWithLedgerPage from "./AccessWithLedgerPage"
import NewWalletPage from "./NewWalletPage"
import RecoverWalletPage from "./RecoverWalletPage"
import ImportWalletPage from "./ImportWalletPage"
+import NewMultisigWalletPage from "./NewMultisigWalletPage"
/* manage */
import ExportWalletPage from "./ExportWalletPage"
@@ -24,6 +25,7 @@ const Auth = () => {
} />
} />
} />
+ } />
{/* manage */}
} />
diff --git a/src/extension/auth/NewMultisigWalletPage.tsx b/src/extension/auth/NewMultisigWalletPage.tsx
new file mode 100644
index 000000000..438c73780
--- /dev/null
+++ b/src/extension/auth/NewMultisigWalletPage.tsx
@@ -0,0 +1,15 @@
+import { useTranslation } from "react-i18next"
+import NewMultisigWalletForm from "auth/modules/create/NewMultisigWalletForm"
+import ExtensionPage from "../components/ExtensionPage"
+
+const NewMultisigWalletPage = () => {
+ const { t } = useTranslation()
+
+ return (
+
+
+
+ )
+}
+
+export default NewMultisigWalletPage
diff --git a/src/extension/auth/SwitchWallet.tsx b/src/extension/auth/SwitchWallet.tsx
index 6aaccdd16..a81be8f40 100644
--- a/src/extension/auth/SwitchWallet.tsx
+++ b/src/extension/auth/SwitchWallet.tsx
@@ -1,4 +1,6 @@
-import { useAuth } from "auth"
+import { isWallet, useAuth } from "auth"
+import { Flex } from "components/layout"
+import MultisigBadge from "auth/components/MultisigBadge"
import { clearStoredPassword } from "../storage"
import ExtensionList from "../components/ExtensionList"
@@ -7,13 +9,23 @@ const SwitchWallet = () => {
const list = wallets
.filter(({ name }) => name !== connectedWallet?.name)
- .map(({ name, address }) => {
+ .map((wallet) => {
const select = () => {
connect(name)
clearStoredPassword()
}
- return { children: name, description: address, onClick: select }
+ const { name, address } = wallet
+ return {
+ children: (
+
+ {isWallet.multisig(wallet) && }
+ {name}
+
+ ),
+ description: address,
+ onClick: select,
+ }
})
return
diff --git a/src/extension/components/ExtensionList.module.scss b/src/extension/components/ExtensionList.module.scss
index 287f91a17..a55103b20 100644
--- a/src/extension/components/ExtensionList.module.scss
+++ b/src/extension/components/ExtensionList.module.scss
@@ -11,7 +11,7 @@
@include flex(space-between);
color: var(--text);
- padding: 20px;
+ padding: 16px 20px;
text-align: left;
transition: background var(--transition);
width: 100%;
diff --git a/src/extension/components/ExtensionList.tsx b/src/extension/components/ExtensionList.tsx
index e93d047ac..10ca79de3 100644
--- a/src/extension/components/ExtensionList.tsx
+++ b/src/extension/components/ExtensionList.tsx
@@ -5,7 +5,7 @@ import { Flex, Grid } from "components/layout"
import styles from "./ExtensionList.module.scss"
interface DefaultItemProps {
- children: string
+ children: ReactNode
description?: string
icon?: ReactNode
active?: boolean
@@ -22,7 +22,10 @@ interface ButtonItem extends DefaultItemProps {
type Item = LinkItem | ButtonItem
const ExtensionList = ({ list }: { list: Item[] }) => {
- const renderItem = ({ children, description, icon, ...item }: Item) => {
+ const renderItem = (
+ { children, description, icon, ...item }: Item,
+ index: number
+ ) => {
const props = {
className: styles.item,
children: (
@@ -32,14 +35,14 @@ const ExtensionList = ({ list }: { list: Item[] }) => {
{children}
- {description}
+ {description && {description}
}
>
),
- key: children,
+ key: index,
}
return "to" in item ? (
diff --git a/src/extension/components/WalletCard.tsx b/src/extension/components/WalletCard.tsx
index b125ae9f4..7548efad5 100644
--- a/src/extension/components/WalletCard.tsx
+++ b/src/extension/components/WalletCard.tsx
@@ -1,6 +1,7 @@
import { ReactNode } from "react"
import { Flex, Grid } from "components/layout"
-import { useAuth } from "auth"
+import { isWallet, useAuth } from "auth"
+import MultisigBadge from "auth/components/MultisigBadge"
import Copy from "./Copy"
import styles from "./WalletCard.module.scss"
import WalletQR from "./WalletQR"
@@ -14,12 +15,18 @@ const WalletCard = ({ extra }: Props) => {
if (!wallet) return null
const { address } = wallet
- const name = "name" in wallet ? wallet.name : undefined
+ const name = isWallet.local(wallet) ? wallet.name : undefined
return (
- {name && {name}
}
+ {name && (
+
+ {isWallet.multisig(wallet) && }
+ {name}
+
+ )}
+
{address}
diff --git a/src/extension/modules/ConfirmTx.tsx b/src/extension/modules/ConfirmTx.tsx
index 27ba5d9b1..fc6d4ee84 100644
--- a/src/extension/modules/ConfirmTx.tsx
+++ b/src/extension/modules/ConfirmTx.tsx
@@ -1,5 +1,6 @@
import { useEffect, useState } from "react"
import { useTranslation } from "react-i18next"
+import { useNavigate } from "react-router-dom"
import { useForm } from "react-hook-form"
import { getErrorMessage } from "utils/error"
import { useThemeAnimation } from "data/settings/Theme"
@@ -8,9 +9,10 @@ import { Flex, FlexColumn, Grid } from "components/layout"
import { Form, FormError, FormItem, FormWarning } from "components/form"
import { Input, Checkbox } from "components/form"
import Overlay from "app/components/Overlay"
-import { useAuth } from "auth"
+import useToPostMultisigTx from "pages/multisig/utils/useToPostMultisigTx"
+import { isWallet, useAuth } from "auth"
import { PasswordError } from "auth/scripts/keystore"
-import { getStoredPassword } from "../storage"
+import { getOpenURL, getStoredPassword } from "../storage"
import { getIsDangerousTx, SignBytesRequest, TxRequest } from "../utils"
import { useRequest } from "../RequestContainer"
import ExtensionPage from "../components/ExtensionPage"
@@ -26,7 +28,7 @@ const ConfirmTx = (props: TxRequest | SignBytesRequest) => {
const animation = useThemeAnimation()
const { wallet, ...auth } = useAuth()
const { actions } = useRequest()
- const passwordRequired = wallet && "name" in wallet
+ const passwordRequired = isWallet.single(wallet)
/* form */
const form = useForm({
@@ -58,6 +60,8 @@ const ConfirmTx = (props: TxRequest | SignBytesRequest) => {
? t("Enter password")
: ""
+ const navigate = useNavigate()
+ const toPostMultisigTx = useToPostMultisigTx()
const submit = async ({ password }: Values) => {
setSubmitting(true)
@@ -66,9 +70,19 @@ const ConfirmTx = (props: TxRequest | SignBytesRequest) => {
try {
if (disabled) throw new Error(disabled)
- const result = await auth[requestType](tx, password)
- const response = { result, success: true }
- actions.tx(requestType, props, response, nextPassword)
+
+ if (isWallet.multisig(wallet)) {
+ const unsignedTx = await auth.create(props.tx)
+ const { pathname, search } = toPostMultisigTx(unsignedTx)
+ const openURL = getOpenURL([pathname, search].join("?"))
+ actions.multisigTx(props)
+ if (openURL) openURL()
+ else navigate({ pathname, search })
+ } else {
+ const result = await auth[requestType](tx, password)
+ const response = { result, success: true }
+ actions.tx(requestType, props, response, nextPassword)
+ }
} catch (error) {
if (error instanceof PasswordError) {
setIncorrect(error.message)
@@ -118,8 +132,7 @@ const ConfirmTx = (props: TxRequest | SignBytesRequest) => {
const error =
props.requestType === "signBytes" &&
- wallet &&
- "ledger" in wallet &&
+ isWallet.ledger(wallet) &&
t("Arbitrary data cannot be signed by Ledger")
const SIZE = { width: 100, height: 100 }
@@ -127,7 +140,7 @@ const ConfirmTx = (props: TxRequest | SignBytesRequest) => {
- {wallet && "ledger" in wallet && {t("Confirm in ledger")}
}
+ {isWallet.ledger(wallet) && {t("Confirm in ledger")}
}
) : (
diff --git a/src/extension/modules/Front.tsx b/src/extension/modules/Front.tsx
index 62acb0f4b..7468efaec 100644
--- a/src/extension/modules/Front.tsx
+++ b/src/extension/modules/Front.tsx
@@ -14,7 +14,7 @@ const Front = () => {
const { requests } = useRequest()
const { connect, tx } = requests
- if (!wallet)
+ if (!wallet) {
return (
@@ -23,6 +23,7 @@ const Front = () => {
)
+ }
if (connect) return
if (tx) return
diff --git a/src/extension/modules/Welcome.module.scss b/src/extension/modules/Welcome.module.scss
index e0571a876..3b5f5c2b4 100644
--- a/src/extension/modules/Welcome.module.scss
+++ b/src/extension/modules/Welcome.module.scss
@@ -1,6 +1,5 @@
.component {
- padding-top: 32px;
- padding-bottom: 8px;
+ padding-top: 8px;
}
.content {
diff --git a/src/extension/modules/Welcome.tsx b/src/extension/modules/Welcome.tsx
index d74e028a8..fa2370315 100644
--- a/src/extension/modules/Welcome.tsx
+++ b/src/extension/modules/Welcome.tsx
@@ -1,15 +1,15 @@
import { useTranslation } from "react-i18next"
-import { useThemeFavicon } from "data/settings/Theme"
+import { useThemeFront } from "data/settings/Theme"
import { FlexColumn } from "components/layout"
import styles from "./Welcome.module.scss"
const Welcome = () => {
const { t } = useTranslation()
- const favicon = useThemeFavicon()
+ const front = useThemeFront()
return (
-
-
+
+
{t("Connect to Terra blockchain")}
)
diff --git a/src/styles/themes/Blossom/Front.png b/src/styles/themes/Blossom/Front.png
new file mode 100644
index 000000000..fc4a8fb53
Binary files /dev/null and b/src/styles/themes/Blossom/Front.png differ
diff --git a/src/styles/themes/Dark/Front.png b/src/styles/themes/Dark/Front.png
new file mode 100644
index 000000000..7cda29122
Binary files /dev/null and b/src/styles/themes/Dark/Front.png differ
diff --git a/src/styles/themes/Light/Front.png b/src/styles/themes/Light/Front.png
new file mode 100644
index 000000000..c150625e5
Binary files /dev/null and b/src/styles/themes/Light/Front.png differ
diff --git a/src/styles/themes/Madness/Front.png b/src/styles/themes/Madness/Front.png
new file mode 100644
index 000000000..585fa3eef
Binary files /dev/null and b/src/styles/themes/Madness/Front.png differ
diff --git a/src/styles/themes/Moon/Front.png b/src/styles/themes/Moon/Front.png
new file mode 100644
index 000000000..9a1f528e4
Binary files /dev/null and b/src/styles/themes/Moon/Front.png differ
diff --git a/src/styles/themes/Whale/Front.png b/src/styles/themes/Whale/Front.png
new file mode 100644
index 000000000..7cb64b194
Binary files /dev/null and b/src/styles/themes/Whale/Front.png differ
diff --git a/src/styles/themes/themes.tsx b/src/styles/themes/themes.tsx
index 9026439f8..365abc6dc 100644
--- a/src/styles/themes/themes.tsx
+++ b/src/styles/themes/themes.tsx
@@ -17,6 +17,14 @@ import FaviconMoon from "./Moon/favicon.svg"
import FaviconWhale from "./Whale/favicon.svg"
import FaviconMadness from "./Madness/favicon.svg"
+/* favicon */
+import FrontLight from "./Light/Front.png"
+import FrontDark from "./Dark/Front.png"
+import FrontBlossom from "./Blossom/Front.png"
+import FrontMoon from "./Moon/Front.png"
+import FrontWhale from "./Whale/Front.png"
+import FrontMadness from "./Madness/Front.png"
+
/* preview */
import { ReactComponent as PreviewLight } from "./Light/preview.svg"
import { ReactComponent as PreviewDark } from "./Dark/preview.svg"
@@ -30,6 +38,7 @@ export interface Theme {
unlock: Amount
animation: string
favicon: string
+ front: string
preview: ReactNode
}
@@ -39,6 +48,7 @@ export const themes: Theme[] = [
unlock: toAmount("0"),
animation: AnimationLight,
favicon: FaviconLight,
+ front: FrontLight,
preview: ,
},
{
@@ -46,6 +56,7 @@ export const themes: Theme[] = [
unlock: toAmount("0"),
animation: AnimationDark,
favicon: FaviconDark,
+ front: FrontDark,
preview: ,
},
{
@@ -53,6 +64,7 @@ export const themes: Theme[] = [
unlock: toAmount("1"),
animation: AnimationBlossom,
favicon: FaviconBlossom,
+ front: FrontBlossom,
preview: ,
},
{
@@ -60,6 +72,7 @@ export const themes: Theme[] = [
unlock: toAmount("10"),
animation: AnimationMoon,
favicon: FaviconMoon,
+ front: FrontMoon,
preview: ,
},
{
@@ -67,6 +80,7 @@ export const themes: Theme[] = [
unlock: toAmount("100"),
animation: AnimationWhale,
favicon: FaviconWhale,
+ front: FrontWhale,
preview: ,
},
{
@@ -74,6 +88,7 @@ export const themes: Theme[] = [
unlock: toAmount("1000"),
animation: AnimationMadness,
favicon: FaviconMadness,
+ front: FrontMadness,
preview: ,
},
]