From 86b63beac625df9e1867e1605def3f28e502c3c8 Mon Sep 17 00:00:00 2001 From: Calvin Kei Date: Wed, 22 Sep 2021 21:30:13 +0800 Subject: [PATCH 1/8] fix multiple minor issues --- components/AccountStatCard/index.tsx | 43 ++++++++++++++--------- components/ActivitiesTable/Row.tsx | 20 +++++------ components/CreateWalletDialog/index.tsx | 2 +- components/SettingTable/FollowUsTable.tsx | 10 +++--- misc/getWalletAddress.ts | 6 +++- pages/settings/index.tsx | 17 ++++++++- patches/@cosmjs+ledger-amino+0.26.0.patch | 40 +++++++++++++++++++-- 7 files changed, 102 insertions(+), 36 deletions(-) diff --git a/components/AccountStatCard/index.tsx b/components/AccountStatCard/index.tsx index 574b74eb..52659ba3 100644 --- a/components/AccountStatCard/index.tsx +++ b/components/AccountStatCard/index.tsx @@ -12,7 +12,6 @@ import { LineChart, Line, YAxis } from 'recharts' import UpIcon from '@material-ui/icons/ArrowDropUp' import DownIcon from '@material-ui/icons/ArrowDropDown' import useTranslation from 'next-translate/useTranslation' -import last from 'lodash/last' import get from 'lodash/get' import { useRouter } from 'next/router' import { gql, useSubscription } from '@apollo/client' @@ -26,6 +25,7 @@ import { formatPercentage, getTotalBalance, getTotalTokenAmount, + transformGqlAcountBalance, transformValidatorsWithTokenAmount, } from '../../misc/utils' import useAccountsBalancesWithinPeriod from '../../graphql/hooks/useAccountsBalancesWithinPeriod' @@ -37,6 +37,7 @@ import { useWalletsContext } from '../../contexts/WalletsContext' import useIconProps from '../../misc/useIconProps' import DelegationDialog from '../DelegationDialog' import { getValidators } from '../../graphql/queries/validators' +import { getLatestAccountBalance } from '../../graphql/queries/accountBalances' const dailyTimestamps = dateRanges .find((d) => d.title === 'day') @@ -55,32 +56,42 @@ const AccountStatCard: React.FC = ({ account }) => { const { currency } = useGeneralContext() const { updateAccount } = useWalletsContext() const router = useRouter() + // Historic data const { data: [accountWithBalance], loading, } = useAccountsBalancesWithinPeriod([account], dailyTimestamps) const [delegateDialogOpen, setDelegateDialogOpen] = React.useState(false) + const data = createEmptyChartData( + (get(accountWithBalance, 'balances', []) as AccountBalance[]).map((b) => getTotalBalance(b)), + 0, + 1 + ) + // Latest data + const { data: latestData } = useSubscription( + gql` + ${getLatestAccountBalance(account.crypto)} + `, + { variables: { address: account.address } } + ) const { data: validatorsData } = useSubscription( gql` ${getValidators(crypto.name)} ` ) - const latestBalance = last(get(accountWithBalance, 'balances', [])) - const tokenAmounts = getTotalTokenAmount(latestBalance).amount - const usdBalance = getTotalBalance(latestBalance).balance - - const validators = transformValidatorsWithTokenAmount(validatorsData, latestBalance) - const availableTokens = get(latestBalance, 'availableTokens', { - coins: [], - tokens_prices: [], - }) - - const data = createEmptyChartData( - (get(accountWithBalance, 'balances', []) as AccountBalance[]).map((b) => getTotalBalance(b)), - 0, - 1 - ) + const { tokenAmounts, usdBalance, availableTokens, validators } = React.useMemo(() => { + const accountBalance = transformGqlAcountBalance(latestData, Date.now()) + return { + tokenAmounts: getTotalTokenAmount(accountBalance).amount, + usdBalance: getTotalBalance(accountBalance).balance, + validators: transformValidatorsWithTokenAmount(validatorsData, accountBalance), + availableTokens: get(accountBalance, 'availableTokens', { + coins: [], + tokens_prices: [], + }), + } + }, [data]) const firstBalance = get(data, '[0].balance', 0) const diff = Math.abs(usdBalance - firstBalance) diff --git a/components/ActivitiesTable/Row.tsx b/components/ActivitiesTable/Row.tsx index 457851f7..5c76fd13 100644 --- a/components/ActivitiesTable/Row.tsx +++ b/components/ActivitiesTable/Row.tsx @@ -35,7 +35,7 @@ const Row: React.FC = ({ activity, account, crypto, address }) => { <> = ({ activity, account, crypto, address }) => { <> = ({ activity, account, crypto, address }) => { <> = ({ activity, account, crypto, address }) => { <> = ({ activity, account, crypto, address }) => { <> = ({ activity, account, crypto, address }) => { return ( <> - {account.name} + {accountDetail.name} {t(`${activity.tag}Activity`)} @@ -267,7 +267,7 @@ const Row: React.FC = ({ activity, account, crypto, address }) => { <> = ({ activity, account, crypto, address }) => { <> = ({ activity, account, crypto, address }) => { {activity.detail.fromAddress === accountDetail.address ? ( = ({ activity, account, crypto, address }) => { {activity.detail.toAddress === accountDetail.address ? ( = ({ open, onClose, setStage(CommonStage.ImportLedgerWalletStage, undefined, true) // select next crypto } else if (ledgerCryptos.length > ledgerAddresses.length) { - const address = await getWalletAddress('', ledgerCryptos[0], 0, signer) + const address = await getWalletAddress('', ledgerCryptos[0], 0, signer, true) setLedgerCryptosIndex((i) => i + 1) setLedgerAddresses((addresses) => [...addresses, address]) // save wallet on last crypto diff --git a/components/SettingTable/FollowUsTable.tsx b/components/SettingTable/FollowUsTable.tsx index 466b8c08..d5779c85 100644 --- a/components/SettingTable/FollowUsTable.tsx +++ b/components/SettingTable/FollowUsTable.tsx @@ -27,7 +27,7 @@ const FollowUsTable: React.FC = () => { target="_blank" rel="noopener noreferrer" > - + { target="_blank" rel="noopener noreferrer" > - + { target="_blank" rel="noopener noreferrer" > - + @@ -90,7 +90,7 @@ const FollowUsTable: React.FC = () => { target="_blank" rel="noopener noreferrer" > - + @@ -108,7 +108,7 @@ const FollowUsTable: React.FC = () => { target="_blank" rel="noopener noreferrer" > - + diff --git a/misc/getWalletAddress.ts b/misc/getWalletAddress.ts index 9c9bb521..f0cae36e 100644 --- a/misc/getWalletAddress.ts +++ b/misc/getWalletAddress.ts @@ -7,7 +7,8 @@ const getWalletAddress = async ( mnemonic: string, crypto: string, index: number, - ledgerTransport?: any + ledgerTransport?: any, + showAddressOnLedger?: boolean ): Promise => { // if (crypto === 'SOL') { // const { getPubkeyFromConfig, SignerConfig } = await import('bd-solana-wasm') @@ -28,6 +29,9 @@ const getWalletAddress = async ( } as any) } const accounts = await signer.getAccounts() + if (showAddressOnLedger) { + await signer.showAddressAndPubKey(accounts[0].address, cryptocurrencies[crypto].prefix) + } return accounts[0].address } diff --git a/pages/settings/index.tsx b/pages/settings/index.tsx index 26744c94..7564e6c5 100644 --- a/pages/settings/index.tsx +++ b/pages/settings/index.tsx @@ -1,15 +1,30 @@ import React from 'react' -import { Box, Typography } from '@material-ui/core' +import { Box, IconButton, Typography, useTheme } from '@material-ui/core' import useTranslation from 'next-translate/useTranslation' +import { useRouter } from 'next/router' +import get from 'lodash/get' +import BackIcon from '../../assets/images/icons/icon_back.svg' import Layout from '../../components/Layout' import SettingTable from '../../components/SettingTable' +import useIconProps from '../../misc/useIconProps' const Setting: React.FC = () => { const { t } = useTranslation('common') + const theme = useTheme() + const iconProps = useIconProps(3, theme.palette.grey[900]) + // Show back btn for chrome extension + const router = useRouter() + const hideMenuQueryParam = get(router, 'query.hideMenu', '') + const isChromeExt = hideMenuQueryParam || (process.browser && (window as any).hideMenu) return ( + {isChromeExt ? ( + router.back()}> + + + ) : null} {t('settings')} diff --git a/patches/@cosmjs+ledger-amino+0.26.0.patch b/patches/@cosmjs+ledger-amino+0.26.0.patch index 66970a05..c61ba56a 100644 --- a/patches/@cosmjs+ledger-amino+0.26.0.patch +++ b/patches/@cosmjs+ledger-amino+0.26.0.patch @@ -11,7 +11,7 @@ index c4a2bab..3149514 100644 export declare class LaunchpadLedger { private readonly testModeAllowed; diff --git a/node_modules/@cosmjs/ledger-amino/build/launchpadledger.js b/node_modules/@cosmjs/ledger-amino/build/launchpadledger.js -index 27173da..4866e83 100644 +index 27173da..76bd289 100644 --- a/node_modules/@cosmjs/ledger-amino/build/launchpadledger.js +++ b/node_modules/@cosmjs/ledger-amino/build/launchpadledger.js @@ -19,15 +19,17 @@ const cosmosBech32Prefix = "cosmos"; @@ -33,7 +33,22 @@ index 27173da..4866e83 100644 this.app = new ledger_cosmos_js_1.default(transport); } async getCosmosAppVersion() { -@@ -87,8 +89,8 @@ class LaunchpadLedger { +@@ -49,6 +51,14 @@ class LaunchpadLedger { + this.handleLedgerErrors(response); + return Uint8Array.from(response.compressed_pk); + } ++ async showAddressAndPubKey(hdPath, hrp) { ++ await this.verifyDeviceIsReady(); ++ utils_1.assert(this.app, "Cosmos Ledger App is not connected"); ++ const hdPathToUse = hdPath || this.hdPaths[0]; ++ // ledger-cosmos-js hardens the first three indices ++ const response = await this.app.showAddressAndPubKey(unharden(hdPathToUse), hrp); ++ return response ++ } + async getPubkeys() { + return this.hdPaths.reduce((promise, hdPath) => promise.then(async (pubkeys) => [...pubkeys, await this.getPubkey(hdPath)]), Promise.resolve([])); + } +@@ -87,8 +97,8 @@ class LaunchpadLedger { if (appName.toLowerCase() === `dashboard`) { throw new Error(`Please open the Cosmos Ledger app on your Ledger device.`); } @@ -44,3 +59,24 @@ index 27173da..4866e83 100644 } } async verifyDeviceIsReady() { +diff --git a/node_modules/@cosmjs/ledger-amino/build/ledgersigner.js b/node_modules/@cosmjs/ledger-amino/build/ledgersigner.js +index 2368724..45f50a5 100644 +--- a/node_modules/@cosmjs/ledger-amino/build/ledgersigner.js ++++ b/node_modules/@cosmjs/ledger-amino/build/ledgersigner.js +@@ -19,6 +19,16 @@ class LedgerSigner { + } + return this.accounts; + } ++ async showAddressAndPubKey(address, hrp) { ++ const accounts = this.accounts || (await this.getAccounts()); ++ const accountIndex = accounts.findIndex((account) => account.address === address); ++ if (accountIndex === -1) { ++ throw new Error(`Address ${address} not found in wallet`); ++ } ++ console.log(this.hdPaths[accountIndex]) ++ const response = await this.ledger.showAddressAndPubKey(this.hdPaths[accountIndex], hrp) ++ return response ++ } + async signAmino(signerAddress, signDoc) { + const accounts = this.accounts || (await this.getAccounts()); + const accountIndex = accounts.findIndex((account) => account.address === signerAddress); From f445146aff25f385cbf5bbde00a9f1c6023ae417 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Sep 2021 21:31:28 +0800 Subject: [PATCH 2/8] Bump tmpl from 1.0.4 to 1.0.5 (#327) Bumps [tmpl](https://github.com/daaku/nodejs-tmpl) from 1.0.4 to 1.0.5. - [Release notes](https://github.com/daaku/nodejs-tmpl/releases) - [Commits](https://github.com/daaku/nodejs-tmpl/commits/v1.0.5) --- updated-dependencies: - dependency-name: tmpl dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: Calvin Kei Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 1e3e85c2..146c3ebc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8860,9 +8860,9 @@ tmp@~0.2.1: rimraf "^3.0.0" tmpl@1.0.x: - version "1.0.4" - resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" - integrity sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE= + version "1.0.5" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" + integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== to-arraybuffer@^1.0.0: version "1.0.1" From 2f4ce6a9bda5e467794c39c66c8fae804cf67a3f Mon Sep 17 00:00:00 2001 From: Calvin Kei Date: Wed, 22 Sep 2021 22:41:06 +0800 Subject: [PATCH 3/8] set withdraw address tx --- components/AddressSendDialog/index.tsx | 17 +- .../SetWithdrawAddressContent.tsx | 40 +++++ .../ConfirmStageContent/index.tsx | 8 + components/EditAccountDialog/AccountInfo.tsx | 20 ++- components/EditAccountDialog/ConfirmEdit.tsx | 98 ----------- .../EditAccountDialog/SecurityPassword.tsx | 61 ------- components/EditAccountDialog/Success.tsx | 41 ----- components/EditAccountDialog/index.tsx | 159 +++++------------- custom.d.ts | 9 + graphql/queries/withdrawAddress.ts | 8 + locales/en/common.json | 3 +- misc/cryptocurrencies.dev.ts | 1 + misc/cryptocurrencies.prod.ts | 1 + misc/cryptocurrencies.ts | 23 +-- pages/account/[address].tsx | 2 +- 15 files changed, 142 insertions(+), 349 deletions(-) create mode 100644 components/ConfirmTransactionDialog/ConfirmStageContent/SetWithdrawAddressContent.tsx delete mode 100644 components/EditAccountDialog/ConfirmEdit.tsx delete mode 100644 components/EditAccountDialog/SecurityPassword.tsx delete mode 100644 components/EditAccountDialog/Success.tsx create mode 100644 graphql/queries/withdrawAddress.ts diff --git a/components/AddressSendDialog/index.tsx b/components/AddressSendDialog/index.tsx index ba8b0797..ec767f76 100644 --- a/components/AddressSendDialog/index.tsx +++ b/components/AddressSendDialog/index.tsx @@ -71,7 +71,7 @@ const AddressSendDialog: React.FC = ({ open, onClose, ad const { availableAmount, availableTokens } = getAvailableAmount() const onConfirm = React.useCallback( - async (r: { amount: { amount: number; denom: string }; address: string }, m: string) => { + async (r: { amount: { amount: number; denom: string }; address: string }) => { try { setLoading(true) const coinsToSend = getEquivalentCoinToSend( @@ -125,16 +125,13 @@ const AddressSendDialog: React.FC = ({ open, onClose, ad noValidate onSubmit={(e) => { e.preventDefault() - onConfirm( - { - address: sender.address, - amount: { - amount: Number(amount), - denom: crypto.name, - }, + onConfirm({ + address: sender.address, + amount: { + amount: Number(amount), + denom: crypto.name, }, - memo - ) + }) }} > diff --git a/components/ConfirmTransactionDialog/ConfirmStageContent/SetWithdrawAddressContent.tsx b/components/ConfirmTransactionDialog/ConfirmStageContent/SetWithdrawAddressContent.tsx new file mode 100644 index 00000000..db549e4f --- /dev/null +++ b/components/ConfirmTransactionDialog/ConfirmStageContent/SetWithdrawAddressContent.tsx @@ -0,0 +1,40 @@ +import React from 'react' +import { Box, Divider, Typography, useTheme } from '@material-ui/core' +import useTranslation from 'next-translate/useTranslation' +import WithdrawIcon from '../../../assets/images/icons/icon_withdraw_tx.svg' +import ValidatorAvatar from '../../ValidatorAvatar' +import cryptocurrencies from '../../../misc/cryptocurrencies' + +interface SetWithdrawAddressContentProps { + msgs: TransactionMsgSetWithdrawAddress[] + account: Account +} + +const SetWithdrawAddressContent: React.FC = ({ msgs, account }) => { + const { t } = useTranslation('common') + + return ( + <> + + {t('edit reward address')} + + + {msgs.map((msg, i) => ( + + + {t('delegator address')} + {msg.value.delegatorAddress} + + + + {t('new reward address')} + {msg.value.withdrawAddress} + + + + ))} + + ) +} + +export default SetWithdrawAddressContent diff --git a/components/ConfirmTransactionDialog/ConfirmStageContent/index.tsx b/components/ConfirmTransactionDialog/ConfirmStageContent/index.tsx index 9c32dbb6..db03db00 100644 --- a/components/ConfirmTransactionDialog/ConfirmStageContent/index.tsx +++ b/components/ConfirmTransactionDialog/ConfirmStageContent/index.tsx @@ -24,6 +24,7 @@ import SubmitProposalContent from './SubmitProposalContent' import VoteContent from './VoteContent' import DepositContent from './DepositContent' import SaveProfileContent from './SaveProfileContent' +import SetWithdrawAddressContent from './SetWithdrawAddressContent' const ReactJson = dynamic(() => import('react-json-view'), { ssr: false }) @@ -130,6 +131,13 @@ const ConfirmStageContent: React.FC = ({ ) case '/desmos.profiles.v1beta1.MsgSaveProfile': return + case '/cosmos.distribution.v1beta1.MsgSetWithdrawAddress': + return ( + + ) default: return null } diff --git a/components/EditAccountDialog/AccountInfo.tsx b/components/EditAccountDialog/AccountInfo.tsx index bae46d7f..433ad0b1 100644 --- a/components/EditAccountDialog/AccountInfo.tsx +++ b/components/EditAccountDialog/AccountInfo.tsx @@ -15,10 +15,11 @@ import useStyles from './styles' interface AccountInfoProps { onEdit(): void onRemove(): void - onSave(moniker): void - onShare(): void + onSave(moniker: string): void + onShare(address: string): void onDetail(): void account: Account + withdrawAddress: string } const AccountInfo: React.FC = ({ @@ -28,6 +29,7 @@ const AccountInfo: React.FC = ({ onSave, onShare, onDetail, + withdrawAddress, }) => { const { t } = useTranslation('common') const classes = useStyles() @@ -61,7 +63,11 @@ const AccountInfo: React.FC = ({ {account.address} - @@ -73,10 +79,14 @@ const AccountInfo: React.FC = ({ {t('reward address')} - {account.address} + {withdrawAddress} - diff --git a/components/EditAccountDialog/ConfirmEdit.tsx b/components/EditAccountDialog/ConfirmEdit.tsx deleted file mode 100644 index 7bf77ea8..00000000 --- a/components/EditAccountDialog/ConfirmEdit.tsx +++ /dev/null @@ -1,98 +0,0 @@ -import { Box, DialogContent, Typography, DialogActions, Button, Divider } from '@material-ui/core' -import useTranslation from 'next-translate/useTranslation' -import React from 'react' -import dynamic from 'next/dynamic' -import useStyles from './styles' -import { useGeneralContext } from '../../contexts/GeneralContext' -import { formatTokenAmount } from '../../misc/utils' - -const ReactJson = dynamic(() => import('react-json-view'), { ssr: false }) - -interface ConfirmEditProps { - currentRewardAddress: string - newRewardAddress: string - memo?: string - gasFee: TokenAmount - onConfirm(r: string): void - rawTransactionData: any - denom: string -} - -const ConfirmEdit: React.FC = ({ - currentRewardAddress, - newRewardAddress, - memo, - gasFee, - onConfirm, - rawTransactionData, - denom, -}) => { - const { t, lang } = useTranslation('common') - const classes = useStyles() - const { theme: themeSetting } = useGeneralContext() - const [viewingData, setViewingData] = React.useState(false) - - return ( - <> - - - - - {t('current reward address')} - {currentRewardAddress} - - - - {t('new reward address')} - {newRewardAddress} - - - - {t('memo')} - {memo} - - - - {t('fee')} - - {formatTokenAmount(gasFee, denom, lang)} - - - - - - - - {viewingData ? ( - - - - ) : null} - - - - - - - ) -} - -export default ConfirmEdit diff --git a/components/EditAccountDialog/SecurityPassword.tsx b/components/EditAccountDialog/SecurityPassword.tsx deleted file mode 100644 index 882ca1ec..00000000 --- a/components/EditAccountDialog/SecurityPassword.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import { - Button, - DialogActions, - DialogContent, - DialogContentText, - Box, - CircularProgress, - useTheme, -} from '@material-ui/core' -import useTranslation from 'next-translate/useTranslation' -import React from 'react' -import PasswordInput from '../PasswordInput' -import useStyles from './styles' - -interface SecurityPasswordProps { - onConfirm(password: string): void - loading: boolean -} - -const SecurityPassword: React.FC = ({ onConfirm, loading }) => { - const { t } = useTranslation('common') - const classes = useStyles() - const theme = useTheme() - const [password, setPassword] = React.useState('') - - return ( -
{ - e.preventDefault() - onConfirm(password) - }} - > - - {t('enter security password')} - - setPassword(e.target.value)} - /> - - - - - - - -
- ) -} - -export default SecurityPassword diff --git a/components/EditAccountDialog/Success.tsx b/components/EditAccountDialog/Success.tsx deleted file mode 100644 index 779dfd36..00000000 --- a/components/EditAccountDialog/Success.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { Box, Button, DialogActions, DialogContent, Typography } from '@material-ui/core' -import useTranslation from 'next-translate/useTranslation' -import React from 'react' -import SuccessLight from '../../assets/images/tx_success_light.svg' -import SuccessDark from '../../assets/images/tx_success_dark.svg' -import useStyles from './styles' -import { useGeneralContext } from '../../contexts/GeneralContext' - -interface ConfirmSendProps { - onClose(): void - denom: string -} - -const Success: React.FC = ({ onClose, denom }) => { - const { t } = useTranslation('common') - const classes = useStyles() - const { theme } = useGeneralContext() - - return ( - <> - - - {theme === 'dark' ? : } - - {t('success')} - - {t('successfully delegated', { denom })} - - - - - - - - - ) -} - -export default Success diff --git a/components/EditAccountDialog/index.tsx b/components/EditAccountDialog/index.tsx index 769fa18b..e8942754 100644 --- a/components/EditAccountDialog/index.tsx +++ b/components/EditAccountDialog/index.tsx @@ -2,32 +2,27 @@ import { Dialog, DialogTitle, IconButton, DialogContent, Box, Typography } from '@material-ui/core' import useTranslation from 'next-translate/useTranslation' import React from 'react' +import invoke from 'lodash/invoke' import get from 'lodash/get' +import { useSubscription, gql } from '@apollo/client' import CloseIcon from '../../assets/images/icons/icon_cross.svg' import BackIcon from '../../assets/images/icons/icon_back.svg' import useStyles from './styles' import useIconProps from '../../misc/useIconProps' import ShareAddress from './ShareAddress' import useStateHistory from '../../misc/useStateHistory' -import { getTokenAmountFromDenoms } from '../../misc/utils' -import cryptocurrencies from '../../misc/cryptocurrencies' import { useWalletsContext } from '../../contexts/WalletsContext' import AccountInfo from './AccountInfo' import useIsMobile from '../../misc/useIsMobile' import EditRewardAddress from './EditRewardAddress' -import ConfirmEdit from './ConfirmEdit' import RemoveAccount from './RemoveAccount' -import Success from '../Success' -import SecurityPassword from '../SecurityPasswordDialogContent' +import { getWithdrawAddress } from '../../graphql/queries/withdrawAddress' enum EditAccountStage { AccountInfoStage = 'account info', RewardAddressIntroStage = 'reward address intro', AddressSharingStage = 'address sharing', EditRewardAddressStage = 'edit reward address', - ConfiremEditStage = 'confirm edit', - SecurityPasswordStage = 'security password', - SuccessStage = 'success', RemoveAccountStage = 'remove account stage', } @@ -35,7 +30,6 @@ interface EditAccountDialogProps { account: Account open: boolean onClose(): void - availableTokens: AvailableTokens } interface Content { @@ -44,30 +38,32 @@ interface Content { dialogWidth?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' } -const EditAccountDialog: React.FC = ({ - account, - open, - onClose, - availableTokens, -}) => { +const EditAccountDialog: React.FC = ({ account, open, onClose }) => { const { t } = useTranslation('common') const classes = useStyles() const iconProps = useIconProps() const { updateAccount, password } = useWalletsContext() const isMobile = useIsMobile() - const crypto = account ? cryptocurrencies[account.crypto] : Object.values(cryptocurrencies)[0] - const [memo, setMemo] = React.useState('') - const [rewardAddress, setRewardAddress] = React.useState('') + + const { data } = useSubscription( + gql` + ${getWithdrawAddress(account.crypto)} + `, + { variables: { address: account.address } } + ) + const withdrawAddress = get(data, 'delegation_reward[0].withdraw_address', account.address) + const [loading, setLoading] = React.useState(false) const [stage, setStage, toPrevStage, isPrevStageAvailable] = useStateHistory( EditAccountStage.AccountInfoStage ) + const [sharingAddress, setSharingAddress] = React.useState(account.address) const saveMoniker = React.useCallback( - async (n: string) => { + async (name: string) => { try { - await updateAccount(account.address, { name: n }) + await updateAccount(account.address, { name }) onClose() } catch (err) { console.log(err) @@ -77,79 +73,27 @@ const EditAccountDialog: React.FC = ({ ) const editRewardAddress = React.useCallback( - (r: string, m: string) => { - setRewardAddress(r) - setMemo(m) - setStage(EditAccountStage.ConfiremEditStage) - }, - [setStage, setRewardAddress, rewardAddress] - ) - - const confirmEdit = React.useCallback(() => { - // add sendTransactionMessage - setStage(EditAccountStage.SecurityPasswordStage) - }, [setStage]) - - // add update reward address in chrome ext and add transactionData - const transactionData = React.useMemo( - () => ({ - address: account.address, - password, - // transactions: delegations - // .map((r) => { - // const coinsToSend = getEquivalentCoinToSend( - // { amount: r.amount, denom }, - // availableTokens.coins, - // availableTokens.tokens_prices - // ) - // return formatTransactionMsg(account.crypto, { - // type: 'delegate', - // delegator: account.address, - // validator: r.validator.address, - // ...coinsToSend, - // }) - // }) - // .filter((a) => a), - gasFee: get(crypto, 'defaultGasFee', {}), - memo, - }), - [availableTokens, account, password, memo] - ) - - const { availableAmount, defaultGasFee } = React.useMemo( - () => ({ - availableAmount: getTokenAmountFromDenoms( - availableTokens.coins, - availableTokens.tokens_prices - ), - defaultGasFee: getTokenAmountFromDenoms([], availableTokens.tokens_prices), // TODO - }), - [availableTokens] - ) - - const sendTransactionMessage = React.useCallback( - async (securityPassword: string) => { + async (newWithdrawAddress: string, memo: string) => { try { - // setLoading(true) - // const result = await sendMsgToChromeExt({ - // event: 'signAndBroadcastTransactions', - // data: { - // securityPassword, - // ...transactionData, - // transactions: transactionData.transactions.map((msg) => - // formatTypeUrlTransactionMsg(msg) - // ), - // }, - // }) - // console.log(result) - // setLoading(false) - setStage(EditAccountStage.SuccessStage, true) + setLoading(true) + const msg = { + typeUrl: '/cosmos.distribution.v1beta1.MsgSetWithdrawAddress', + value: { + delegatorAddress: account.address, + withdrawAddress: newWithdrawAddress, + }, + } + await invoke(window, 'forboleX.sendTransaction', password, account.address, { + msgs: [msg], + memo, + }) + setLoading(false) + onClose() } catch (err) { setLoading(false) - console.log(err) } }, - [transactionData] + [setStage, account] ) const content: Content = React.useMemo(() => { @@ -160,21 +104,6 @@ const EditAccountDialog: React.FC = ({ dialogWidth: 'xs', content: , } - case EditAccountStage.SuccessStage: - return { - dialogWidth: 'xs', - content: , - } - case EditAccountStage.SecurityPasswordStage: - return { - content: ( - - ), - } case EditAccountStage.RewardAddressIntroStage: return { title: t('reward address intro title'), @@ -191,7 +120,7 @@ const EditAccountDialog: React.FC = ({ case EditAccountStage.AddressSharingStage: return { title: t('address sharing title'), - content: , + content: , } case EditAccountStage.EditRewardAddressStage: return { @@ -200,21 +129,6 @@ const EditAccountDialog: React.FC = ({ editRewardAddress(r, m)} /> ), } - case EditAccountStage.ConfiremEditStage: - return { - title: t('confirm edit title'), - content: ( - - ), - } case EditAccountStage.AccountInfoStage: default: return { @@ -222,11 +136,15 @@ const EditAccountDialog: React.FC = ({ content: ( setStage(EditAccountStage.EditRewardAddressStage)} onRemove={() => setStage(EditAccountStage.RemoveAccountStage)} onSave={saveMoniker} onDetail={() => setStage(EditAccountStage.RewardAddressIntroStage)} - onShare={() => setStage(EditAccountStage.AddressSharingStage)} + onShare={(address) => { + setStage(EditAccountStage.AddressSharingStage) + setSharingAddress(address) + }} /> ), } @@ -235,7 +153,6 @@ const EditAccountDialog: React.FC = ({ React.useEffect(() => { if (open) { - setMemo('') setLoading(false) setStage(EditAccountStage.AccountInfoStage, true) } diff --git a/custom.d.ts b/custom.d.ts index 924ea515..c195ff30 100644 --- a/custom.d.ts +++ b/custom.d.ts @@ -368,6 +368,14 @@ interface TransactionMsgDeposit { } } +interface TransactionMsgSetWithdrawAddress { + typeUrl: '/cosmos.distribution.v1beta1.MsgSetWithdrawAddress' + value: { + delegatorAddress: string + withdrawAddress: string + } +} + interface TransactionMsgSaveProfile { typeUrl: '/desmos.profiles.v1beta1.MsgSaveProfile' value: { @@ -390,6 +398,7 @@ type TransactionMsg = | TransactionMsgSubmitProposal | TransactionMsgVote | TransactionMsgDeposit + | TransactionMsgSetWithdrawAddress | TransactionMsgSaveProfile interface Transaction { diff --git a/graphql/queries/withdrawAddress.ts b/graphql/queries/withdrawAddress.ts new file mode 100644 index 00000000..ab0c7ee8 --- /dev/null +++ b/graphql/queries/withdrawAddress.ts @@ -0,0 +1,8 @@ +export const getWithdrawAddress = (crypto: string): string => ` + subscription WithdrawAddress($address: String!) { + delegation_reward(where: {delegator_address: {_eq: $address}}, limit: 1, order_by: {height: desc}) { + withdraw_address + delegator_address + } + } +` diff --git a/locales/en/common.json b/locales/en/common.json index 91655383..9144e662 100644 --- a/locales/en/common.json +++ b/locales/en/common.json @@ -391,5 +391,6 @@ "dtag placeholder": "@", "bio": "Bio", "bio placeholder": "Add a bio to your profile", - "profile was saved": "Your profile was saved" + "profile was saved": "Your profile was saved", + "delegator address": "Delegator Address" } diff --git a/misc/cryptocurrencies.dev.ts b/misc/cryptocurrencies.dev.ts index 5550b94c..b1c46343 100644 --- a/misc/cryptocurrencies.dev.ts +++ b/misc/cryptocurrencies.dev.ts @@ -37,6 +37,7 @@ const cryptocurrencies: { [key: string]: Cryptocurrency } = { '/cosmos.gov.v1beta1.MsgSubmitProposal': '400000', '/cosmos.gov.v1beta1.MsgDeposit': '400000', '/cosmos.gov.v1beta1.MsgVote': '400000', + '/cosmos.distribution.v1beta1.MsgSetWithdrawAddress': '400000', '/desmos.profiles.v1beta1.MsgSaveProfile': '400000', }, }, diff --git a/misc/cryptocurrencies.prod.ts b/misc/cryptocurrencies.prod.ts index 07aea155..2e6bea63 100644 --- a/misc/cryptocurrencies.prod.ts +++ b/misc/cryptocurrencies.prod.ts @@ -37,6 +37,7 @@ const cryptocurrencies: { [key: string]: Cryptocurrency } = { '/cosmos.gov.v1beta1.MsgSubmitProposal': '400000', '/cosmos.gov.v1beta1.MsgDeposit': '400000', '/cosmos.gov.v1beta1.MsgVote': '400000', + '/cosmos.distribution.v1beta1.MsgSetWithdrawAddress': '400000', '/desmos.profiles.v1beta1.MsgSaveProfile': '400000', }, }, diff --git a/misc/cryptocurrencies.ts b/misc/cryptocurrencies.ts index 07aea155..b1c46343 100644 --- a/misc/cryptocurrencies.ts +++ b/misc/cryptocurrencies.ts @@ -1,23 +1,23 @@ const cryptocurrencies: { [key: string]: Cryptocurrency } = { - DSM: { - name: 'DSM', + DARIC: { + name: 'DARIC', prefix: 'desmos', ledgerAppName: 'Desmos', ecosystem: 'cosmos', - chainId: 'desmos-mainnet-1', - chainName: 'Desmos Mainnet', + chainId: 'morpheus-apollo-2', + chainName: 'Desmos', image: '/static/images/cryptocurrencies/dsm.svg', coinType: 852, - graphqlHttpUrl: 'https://gql.mainnet.desmos.network/v1/graphql', - graphqlWsUrl: 'wss://gql-ws.mainnet.desmos.network/v1/graphql', - blockExplorerBaseUrl: 'https://explorer.desmos.network', - rpcEndpoint: 'https://rpc.mainnet.desmos.network', + graphqlHttpUrl: 'https://gql.morpheus.desmos.network/v1/graphql', + graphqlWsUrl: 'wss://gql.morpheus.desmos.network/v1/graphql', + blockExplorerBaseUrl: 'https://morpheus.desmos.network', + rpcEndpoint: 'https://rpc.morpheus.desmos.network', ibcChains: [ { - name: 'Desmos Mainnet', + name: 'Desmos', image: '/static/images/cryptocurrencies/dsm.svg', channel: 'channel 1', - chainId: 'desmos-mainnet-1', + chainId: 'morpheus-apollo-2', addressPrefix: 'desmos', crypto: 'DSM', }, @@ -25,7 +25,7 @@ const cryptocurrencies: { [key: string]: Cryptocurrency } = { defaultGasFee: { amount: { amount: 0.01, - denom: 'udsm', + denom: 'udaric', }, gas: { '/cosmos.bank.v1beta1.MsgSend': '200000', @@ -37,6 +37,7 @@ const cryptocurrencies: { [key: string]: Cryptocurrency } = { '/cosmos.gov.v1beta1.MsgSubmitProposal': '400000', '/cosmos.gov.v1beta1.MsgDeposit': '400000', '/cosmos.gov.v1beta1.MsgVote': '400000', + '/cosmos.distribution.v1beta1.MsgSetWithdrawAddress': '400000', '/desmos.profiles.v1beta1.MsgSaveProfile': '400000', }, }, diff --git a/pages/account/[address].tsx b/pages/account/[address].tsx index 7a82ae7f..5361d5ff 100644 --- a/pages/account/[address].tsx +++ b/pages/account/[address].tsx @@ -1,4 +1,4 @@ -import { Breadcrumbs, Link as MLink, Card, Box, Typography, Button } from '@material-ui/core' +import { Breadcrumbs, Link as MLink } from '@material-ui/core' import useTranslation from 'next-translate/useTranslation' import Link from 'next/link' import { useRouter } from 'next/router' From 0e8746941ce806972de1fdac535092a2eb8d39e3 Mon Sep 17 00:00:00 2001 From: Calvin Kei Date: Fri, 24 Sep 2021 16:37:03 +0800 Subject: [PATCH 4/8] add loading --- .../EditAccountDialog/EditRewardAddress.tsx | 36 +++++++++++++------ components/EditAccountDialog/index.tsx | 6 +++- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/components/EditAccountDialog/EditRewardAddress.tsx b/components/EditAccountDialog/EditRewardAddress.tsx index c91ad3e3..0cd302df 100644 --- a/components/EditAccountDialog/EditRewardAddress.tsx +++ b/components/EditAccountDialog/EditRewardAddress.tsx @@ -1,17 +1,32 @@ -import { Box, DialogContent, Typography, TextField, DialogActions, Button } from '@material-ui/core' +import { + Box, + DialogContent, + Typography, + TextField, + DialogActions, + Button, + CircularProgress, + useTheme, +} from '@material-ui/core' import useTranslation from 'next-translate/useTranslation' import React from 'react' import useStyles from './styles' interface EditRewardAddressProps { - account: Account + oldWithdrawAddress: string onNext(r: string, m: string): void + loading: boolean } -const EditRewardAddress: React.FC = ({ account, onNext }) => { +const EditRewardAddress: React.FC = ({ + oldWithdrawAddress, + onNext, + loading, +}) => { const { t } = useTranslation('common') const classes = useStyles() - const [rewardAddress, setRewardAddress] = React.useState('') + const theme = useTheme() + const [withdrawAddress, setWithdrawAddress] = React.useState('') const [memo, setMemo] = React.useState('') return ( @@ -19,7 +34,7 @@ const EditRewardAddress: React.FC = ({ account, onNext } noValidate onSubmit={(e) => { e.preventDefault() - onNext(rewardAddress, memo) + onNext(withdrawAddress, memo) }} > @@ -33,7 +48,8 @@ const EditRewardAddress: React.FC = ({ account, onNext } InputProps={{ disableUnderline: true, }} - value={account.address} + value={oldWithdrawAddress} + contentEditable={false} />
@@ -45,8 +61,8 @@ const EditRewardAddress: React.FC = ({ account, onNext } InputProps={{ disableUnderline: true, }} - value={rewardAddress} - onChange={(e) => setRewardAddress(e.target.value)} + value={withdrawAddress} + onChange={(e) => setWithdrawAddress(e.target.value)} /> @@ -72,10 +88,10 @@ const EditRewardAddress: React.FC = ({ account, onNext } variant="contained" className={classes.nextButton} color="primary" - disabled={rewardAddress === ''} + disabled={withdrawAddress === '' || loading} type="submit" > - {t('next')} + {loading ? : t('next')} diff --git a/components/EditAccountDialog/index.tsx b/components/EditAccountDialog/index.tsx index e8942754..0cb5c90b 100644 --- a/components/EditAccountDialog/index.tsx +++ b/components/EditAccountDialog/index.tsx @@ -126,7 +126,11 @@ const EditAccountDialog: React.FC = ({ account, open, on return { title: t('edit reward address'), content: ( - editRewardAddress(r, m)} /> + editRewardAddress(r, m)} + loading={loading} + /> ), } case EditAccountStage.AccountInfoStage: From 7c6e106bfc6cee685cd31327e1b650bc056265a4 Mon Sep 17 00:00:00 2001 From: Calvin Kei Date: Fri, 24 Sep 2021 16:55:53 +0800 Subject: [PATCH 5/8] update styles --- components/EditAccountDialog/EditRewardAddress.tsx | 2 +- misc/globalCss.ts | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/components/EditAccountDialog/EditRewardAddress.tsx b/components/EditAccountDialog/EditRewardAddress.tsx index 0cd302df..dd6dc97d 100644 --- a/components/EditAccountDialog/EditRewardAddress.tsx +++ b/components/EditAccountDialog/EditRewardAddress.tsx @@ -49,7 +49,7 @@ const EditRewardAddress: React.FC = ({ disableUnderline: true, }} value={oldWithdrawAddress} - contentEditable={false} + disabled /> diff --git a/misc/globalCss.ts b/misc/globalCss.ts index ccaf2b90..4ba8d3a5 100644 --- a/misc/globalCss.ts +++ b/misc/globalCss.ts @@ -43,6 +43,9 @@ const GlobalCss = withStyles( '.MuiFilledInput-root.Mui-focused': { backgroundColor: theme.palette.grey[50], }, + '.MuiFilledInput-root.Mui-disabled': { + backgroundColor: theme.palette.grey[50], + }, '.MuiFilledInput-root:hover': { backgroundColor: theme.palette.grey[50], }, From d49f73c59bd3c8610ae2ced12c549c03558e2de0 Mon Sep 17 00:00:00 2001 From: Calvin Kei Date: Fri, 24 Sep 2021 22:48:51 +0800 Subject: [PATCH 6/8] added tx record --- components/ActivitiesTable/Row.tsx | 28 ++++++++++++++++++++++------ locales/en/common.json | 2 +- misc/cryptocurrencies.ts | 22 +++++++++++----------- misc/utils.ts | 17 +++++++++++++++++ 4 files changed, 51 insertions(+), 18 deletions(-) diff --git a/components/ActivitiesTable/Row.tsx b/components/ActivitiesTable/Row.tsx index 5c76fd13..ca9f9303 100644 --- a/components/ActivitiesTable/Row.tsx +++ b/components/ActivitiesTable/Row.tsx @@ -404,13 +404,29 @@ const Row: React.FC = ({ activity, account, crypto, address }) => { if (activity.tag === 'setRewardAddress') { return ( <> - - {activity.detail.srcAddress} - + + + + + {t(`${activity.tag}Activity`)} - - {activity.detail.dstAddress} - + + {activity.detail.withdrawAddress} + ) } diff --git a/locales/en/common.json b/locales/en/common.json index 9144e662..276db565 100644 --- a/locales/en/common.json +++ b/locales/en/common.json @@ -133,7 +133,7 @@ "unjail": "Unjail", "unjailActivity": "unjaied", "setRewardAddress": "Set Reward Address", - "setRewardAddressActivity": "set reward address", + "setRewardAddressActivity": "set reward address to", "submitProposal": "Submit Proposal", "submitProposalActivity": "submitted a proposal", "toTheFollowingRecepients": "to the following recepients;", diff --git a/misc/cryptocurrencies.ts b/misc/cryptocurrencies.ts index b1c46343..2e6bea63 100644 --- a/misc/cryptocurrencies.ts +++ b/misc/cryptocurrencies.ts @@ -1,23 +1,23 @@ const cryptocurrencies: { [key: string]: Cryptocurrency } = { - DARIC: { - name: 'DARIC', + DSM: { + name: 'DSM', prefix: 'desmos', ledgerAppName: 'Desmos', ecosystem: 'cosmos', - chainId: 'morpheus-apollo-2', - chainName: 'Desmos', + chainId: 'desmos-mainnet-1', + chainName: 'Desmos Mainnet', image: '/static/images/cryptocurrencies/dsm.svg', coinType: 852, - graphqlHttpUrl: 'https://gql.morpheus.desmos.network/v1/graphql', - graphqlWsUrl: 'wss://gql.morpheus.desmos.network/v1/graphql', - blockExplorerBaseUrl: 'https://morpheus.desmos.network', - rpcEndpoint: 'https://rpc.morpheus.desmos.network', + graphqlHttpUrl: 'https://gql.mainnet.desmos.network/v1/graphql', + graphqlWsUrl: 'wss://gql-ws.mainnet.desmos.network/v1/graphql', + blockExplorerBaseUrl: 'https://explorer.desmos.network', + rpcEndpoint: 'https://rpc.mainnet.desmos.network', ibcChains: [ { - name: 'Desmos', + name: 'Desmos Mainnet', image: '/static/images/cryptocurrencies/dsm.svg', channel: 'channel 1', - chainId: 'morpheus-apollo-2', + chainId: 'desmos-mainnet-1', addressPrefix: 'desmos', crypto: 'DSM', }, @@ -25,7 +25,7 @@ const cryptocurrencies: { [key: string]: Cryptocurrency } = { defaultGasFee: { amount: { amount: 0.01, - denom: 'udaric', + denom: 'udsm', }, gas: { '/cosmos.bank.v1beta1.MsgSend': '200000', diff --git a/misc/utils.ts b/misc/utils.ts index dca314a9..1024c77f 100644 --- a/misc/utils.ts +++ b/misc/utils.ts @@ -447,6 +447,23 @@ export const transformTransactions = ( success: get(t, 'transaction.success', false), } } + if (t.type.includes('MsgSetWithdrawAddress')) { + return { + ref: `#${get(t, 'transaction_hash', '')}`, + tab: 'distribution', + tag: 'setRewardAddress', + date: `${format( + new Date(get(t, 'transaction.block.timestamp')), + 'dd MMM yyyy HH:mm' + )} UTC`, + detail: { + delegatorAddress: get(t, 'value.delegator_address', ''), + withdrawAddress: get(t, 'value.withdraw_address', ''), + }, + amount: getTokenAmountFromDenoms([get(t, 'value.amount', {})], tokensPrices), + success: get(t, 'transaction.success', false), + } + } return null }) .filter((a) => !!a) From 861cc45018b49854eeef0608d34fe3d1ac95f438 Mon Sep 17 00:00:00 2001 From: Calvin Kei Date: Fri, 24 Sep 2021 22:56:53 +0800 Subject: [PATCH 7/8] fix build --- components/AccountDetailCard/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/components/AccountDetailCard/index.tsx b/components/AccountDetailCard/index.tsx index 8ce8e01c..62038b29 100644 --- a/components/AccountDetailCard/index.tsx +++ b/components/AccountDetailCard/index.tsx @@ -247,7 +247,6 @@ const AccountDetailCard: React.FC = ({ open={editAccountDialogOpen} onClose={() => setEditAccountDialogOpen(false)} account={account} - availableTokens={availableTokens} /> ) From 1c79c617dbe4ee25e72d84daaf2ca98db5490607 Mon Sep 17 00:00:00 2001 From: Calvin Kei Date: Sun, 26 Sep 2021 00:31:11 +0800 Subject: [PATCH 8/8] update version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 030cfebf..93dd72f9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "forbole-x", - "version": "0.5.0", + "version": "0.6.0", "private": true, "scripts": { "dev": "next dev",