From aab92654e0edcbe7f7a18814f24594cdfe52500e Mon Sep 17 00:00:00 2001 From: Manuel Alessandro Collazo Date: Thu, 25 May 2023 17:08:42 +0100 Subject: [PATCH 01/25] fix: send page enter confirmation (#389) --- src/components/form/Input.tsx | 1 + src/components/form/StandardDropDown.tsx | 1 + 2 files changed, 2 insertions(+) diff --git a/src/components/form/Input.tsx b/src/components/form/Input.tsx index c88104746..489d9d13c 100644 --- a/src/components/form/Input.tsx +++ b/src/components/form/Input.tsx @@ -47,6 +47,7 @@ const Input = forwardRef( {actionButton && ( - ) : ( - selectedValidatorsText - )} - - - - {selectable && isOpen && ( - - - {Object.values(state).some((state) => !state) ? ( - - ) : ( - + ) : ( + + )} + + +
+
{t("Validators")}
+
{t("Rewards")}
+
+
+ {byValidator.map(({ address, list: [{ denom, amount }] }) => { + const checked = state[address] + return ( + + setState({ ...state, [address]: !checked }) + } + key={address} > - {t("Deselect all")} - - )} - - -
- {byValidator.map(({ address, sum }) => { - const checked = state[address] - - return ( - - setState({ ...state, [address]: !checked }) - } - key={address} - > -
+
+
-
-
- ) - })} -
- - )} - - - + +
+ +
+ +
+ ) + })} +
+
+ {selected.length ? : undefined} {Object.entries(selectedTotal).map(([denom, amount]) => ( @@ -221,7 +191,6 @@ const WithdrawRewardsForm = ({ rewards, validators, chain }: Props) => { - {fee.render()} {submit.button} From 19fc05ffb81faa97766ece869869b955fb5bdec4 Mon Sep 17 00:00:00 2001 From: Manuel Alessandro Collazo Date: Fri, 26 May 2023 08:28:51 +0100 Subject: [PATCH 03/25] fix: back button navigate (#390) --- src/auth/modules/create/RecoverWalletPage.tsx | 2 +- src/components/layout/Page.tsx | 8 +++++--- src/pages/gov/ProposalDetails.tsx | 11 ++++++----- src/pages/multisig/SignMultisigTxPage.tsx | 2 +- src/pages/stake/ValidatorDetails.tsx | 2 +- src/txs/gov/DepositTx.tsx | 2 +- src/txs/gov/SubmitProposalTx.tsx | 2 +- src/txs/gov/VoteTx.tsx | 2 +- src/txs/stake/StakeTx.tsx | 2 +- src/txs/stake/WithdrawRewards.tsx | 7 ++++++- src/txs/wasm/InstantiateContractTx.tsx | 2 +- src/txs/wasm/StoreCodeTx.tsx | 2 +- 12 files changed, 26 insertions(+), 18 deletions(-) diff --git a/src/auth/modules/create/RecoverWalletPage.tsx b/src/auth/modules/create/RecoverWalletPage.tsx index b0672920a..7770bc6f5 100644 --- a/src/auth/modules/create/RecoverWalletPage.tsx +++ b/src/auth/modules/create/RecoverWalletPage.tsx @@ -6,7 +6,7 @@ const RecoverWalletPage = () => { const { t } = useTranslation() return ( - + diff --git a/src/components/layout/Page.tsx b/src/components/layout/Page.tsx index 44496435f..49eb4a54b 100644 --- a/src/components/layout/Page.tsx +++ b/src/components/layout/Page.tsx @@ -16,7 +16,7 @@ interface Props extends QueryState { small?: boolean sub?: boolean // used as a page in a page invisible?: boolean // used as a page in a page with no margin - backButton?: boolean + backButtonPath?: string } const Page = (props: PropsWithChildren) => { @@ -29,7 +29,7 @@ const Page = (props: PropsWithChildren) => { sub, invisible, mainClassName, - backButton, + backButtonPath, } = props return ( @@ -43,7 +43,9 @@ const Page = (props: PropsWithChildren) => { {title && (
- {backButton && navigate(-1)} />} + {backButtonPath && ( + navigate(backButtonPath)} /> + )}

{title}

{extra} diff --git a/src/pages/gov/ProposalDetails.tsx b/src/pages/gov/ProposalDetails.tsx index ae7752911..9b3879912 100644 --- a/src/pages/gov/ProposalDetails.tsx +++ b/src/pages/gov/ProposalDetails.tsx @@ -21,11 +21,12 @@ const ProposalDetails = () => { useGoBackOnError(state) - const render = () => { - if (!proposal) return null - - const { status } = proposal + if (!proposal) { + return null + } + const { status } = proposal + const render = () => { return ( @@ -70,7 +71,7 @@ const ProposalDetails = () => { {...state} title={t("Proposal details")} extra={proposal && } - backButton + backButtonPath={`/gov#${status}`} > {render()} diff --git a/src/pages/multisig/SignMultisigTxPage.tsx b/src/pages/multisig/SignMultisigTxPage.tsx index fbaf4869a..767297bfe 100644 --- a/src/pages/multisig/SignMultisigTxPage.tsx +++ b/src/pages/multisig/SignMultisigTxPage.tsx @@ -22,7 +22,7 @@ const SignMultisigTxPage = () => { } return ( - + {render()} ) diff --git a/src/pages/stake/ValidatorDetails.tsx b/src/pages/stake/ValidatorDetails.tsx index 363968149..45b89dd61 100644 --- a/src/pages/stake/ValidatorDetails.tsx +++ b/src/pages/stake/ValidatorDetails.tsx @@ -33,7 +33,7 @@ const ValidatorDetails = () => { } return ( - + {render()} ) diff --git a/src/txs/gov/DepositTx.tsx b/src/txs/gov/DepositTx.tsx index 4aab298b7..2f1ff3d46 100644 --- a/src/txs/gov/DepositTx.tsx +++ b/src/txs/gov/DepositTx.tsx @@ -11,7 +11,7 @@ const DepositTx = () => { const { data: proposal, ...state } = useProposal(id, chain) return ( - + diff --git a/src/txs/gov/SubmitProposalTx.tsx b/src/txs/gov/SubmitProposalTx.tsx index 5e7837269..7148273a8 100644 --- a/src/txs/gov/SubmitProposalTx.tsx +++ b/src/txs/gov/SubmitProposalTx.tsx @@ -12,7 +12,7 @@ const SubmitProposalTx = () => { } return ( - + {(chain) => render(chain)} ) diff --git a/src/txs/gov/VoteTx.tsx b/src/txs/gov/VoteTx.tsx index 5c7b9e697..ee46e52fe 100644 --- a/src/txs/gov/VoteTx.tsx +++ b/src/txs/gov/VoteTx.tsx @@ -11,7 +11,7 @@ const VoteTx = () => { const { data: proposal, ...state } = useProposal(id, chain) return ( - + diff --git a/src/txs/stake/StakeTx.tsx b/src/txs/stake/StakeTx.tsx index 355da4152..223dd6a8c 100644 --- a/src/txs/stake/StakeTx.tsx +++ b/src/txs/stake/StakeTx.tsx @@ -109,7 +109,7 @@ const StakeTx = () => { } return ( - + { } return ( - + {render()} ) diff --git a/src/txs/wasm/InstantiateContractTx.tsx b/src/txs/wasm/InstantiateContractTx.tsx index 503c7f835..2aa2ea407 100644 --- a/src/txs/wasm/InstantiateContractTx.tsx +++ b/src/txs/wasm/InstantiateContractTx.tsx @@ -7,7 +7,7 @@ const InstantiateContractTx = () => { const { t } = useTranslation() return ( - + {(chainID) => ( diff --git a/src/txs/wasm/StoreCodeTx.tsx b/src/txs/wasm/StoreCodeTx.tsx index 923beaa42..72597c27d 100644 --- a/src/txs/wasm/StoreCodeTx.tsx +++ b/src/txs/wasm/StoreCodeTx.tsx @@ -6,7 +6,7 @@ const StoreCodeTx = () => { const { t } = useTranslation() return ( - + {(chainID) => } From 0b48a4d63c3e1ee1fd488d7b91cdd44ecd220ef4 Mon Sep 17 00:00:00 2001 From: plubber <51789398+ericHgorski@users.noreply.github.com> Date: Thu, 11 May 2023 11:27:55 -0400 Subject: [PATCH 04/25] fix: always show native assets regardless of denoms (#348) * St 353 display chain selection on inactive chain click in OtherChainButtons (#329) * WIP: chains display prefs open * feat: open preferences on click * cleanup * cleanup * feat: swap terra.kitchen/utils for @terra-money/terra-utils (#321) * feat: guard against empty networks, chaindID, or lcd (#327) * feat: kado integration and updated wallet buttons (#320) * feat: kado integration and updated wallet buttons * Fix passing/not passing * fix prettier * fix: always show native assets regardless of denoms * Update chain.ts * fix: hide low balance * fix: remove log * fix: code readability Changing code to follow logic for improved readability. * fix: exclude cw20 tokens --------- Co-authored-by: Mike <17463738+mwmerz@users.noreply.github.com> Co-authored-by: Alessandro Candeago <54709706+alecande11@users.noreply.github.com> Co-authored-by: Manuel Alessandro Collazo --- src/pages/wallet/AssetList.module.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/wallet/AssetList.module.scss b/src/pages/wallet/AssetList.module.scss index 7ef17312d..ff8868cc6 100644 --- a/src/pages/wallet/AssetList.module.scss +++ b/src/pages/wallet/AssetList.module.scss @@ -4,6 +4,7 @@ background-color: var(--card-bg-muted); padding-inline: 10px; grid-area: list; + display: grid; grid-template-areas: "title" "list"; grid-template-rows: min-content auto; From 257cac8bad42fbbfb3bd555cb95d4f2883b148db Mon Sep 17 00:00:00 2001 From: Joshua Brigati Date: Tue, 16 May 2023 10:15:14 -0500 Subject: [PATCH 05/25] St 355 moon theme (#379) * changes blossom theme colors and changed some font styles mainly font-smoothing and font weight adjusted to reflect antialiased smoothing * Added navigation background and aside blurs ST-309 * changed blossom mp4 location * fixed aside blur on smaller heights * changed where the aside blurs are placed and added better management for mobile and small heights * fixed other changes I wanted in * changes colors * completed moon theme changes. Also implimented two toned cards the are on all themes along with some other small UI changes ST-355 * fixing mobile issues ST-355 * added theme stake amounts back in * changed background blob color to lighter, changed preview svg and removed staking minimums to themes * removed image I forgot to delete --- src/pages/wallet/AssetList.module.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/wallet/AssetList.module.scss b/src/pages/wallet/AssetList.module.scss index ff8868cc6..7ef17312d 100644 --- a/src/pages/wallet/AssetList.module.scss +++ b/src/pages/wallet/AssetList.module.scss @@ -4,7 +4,6 @@ background-color: var(--card-bg-muted); padding-inline: 10px; grid-area: list; - display: grid; grid-template-areas: "title" "list"; grid-template-rows: min-content auto; From 1f726ddc0e57ea13ded90d30fee02e722ded5887 Mon Sep 17 00:00:00 2001 From: Manuel Alessandro Collazo Date: Thu, 11 May 2023 20:17:16 +0100 Subject: [PATCH 06/25] feat: factory token names --- src/data/token.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/data/token.tsx b/src/data/token.tsx index bc5de33ac..b1afc783f 100644 --- a/src/data/token.tsx +++ b/src/data/token.tsx @@ -77,6 +77,8 @@ export const useNativeDenoms = () => { function readNativeDenom(denom: Denom): TokenItem { const fixedDenom = denom.startsWith("ibc/") ? `${readDenom(denom).substring(0, 5)}...` + : denom.startsWith("factory/") + ? denom.split("/").pop()?.slice(1).toUpperCase() : readDenom(denom) // native token From ae419e0d2f09e773f73e429c330074d95e4aa668 Mon Sep 17 00:00:00 2001 From: Manuel Alessandro Collazo Date: Fri, 12 May 2023 16:53:21 +0100 Subject: [PATCH 07/25] feat: factory network icon --- src/data/token.tsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/data/token.tsx b/src/data/token.tsx index b1afc783f..288e8d123 100644 --- a/src/data/token.tsx +++ b/src/data/token.tsx @@ -7,7 +7,8 @@ import { useTokenInfoCW20 } from "./queries/wasm" import { useCustomTokensCW20 } from "./settings/CustomTokens" import { useCW20Whitelist, useIBCWhitelist } from "./Terra/TerraAssets" import { useWhitelist } from "./queries/chains" -import { useNetworkName } from "./wallet" +import { useNetworkName, useNetwork } from "./wallet" +import { getChainIDFromAddress } from "utils/bech32" export const useTokenItem = (token: Token): TokenItem | undefined => { const readNativeDenom = useNativeDenoms() @@ -73,6 +74,7 @@ export const useNativeDenoms = () => { const { whitelist, ibcDenoms, legacyWhitelist } = useWhitelist() const { list: cw20 } = useCustomTokensCW20() const networkName = useNetworkName() + const networks = useNetwork() function readNativeDenom(denom: Denom): TokenItem { const fixedDenom = denom.startsWith("ibc/") @@ -81,6 +83,13 @@ export const useNativeDenoms = () => { ? denom.split("/").pop()?.slice(1).toUpperCase() : readDenom(denom) + let factoryIcon + if (denom.startsWith("factory/")) { + const chainID = getChainIDFromAddress(denom.split("/")[1], networks) + if (chainID) { + factoryIcon = networks[chainID].icon + } + } // native token if (whitelist[networkName]?.[denom]) return whitelist[networkName]?.[denom] @@ -106,6 +115,8 @@ export const useNativeDenoms = () => { name: fixedDenom, icon: denom.startsWith("ibc/") ? "https://assets.terra.money/icon/svg/IBC.svg" + : denom.startsWith("factory/") + ? factoryIcon : "https://assets.terra.money/icon/svg/Terra.svg", decimals: 6, } From 942679d131dc448757d2a98f859bff808be9d4c8 Mon Sep 17 00:00:00 2001 From: Manuel Alessandro Collazo Date: Fri, 12 May 2023 20:46:24 +0100 Subject: [PATCH 08/25] fix: naming test --- src/data/token.tsx | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/data/token.tsx b/src/data/token.tsx index 288e8d123..eb5a9bacf 100644 --- a/src/data/token.tsx +++ b/src/data/token.tsx @@ -77,14 +77,22 @@ export const useNativeDenoms = () => { const networks = useNetwork() function readNativeDenom(denom: Denom): TokenItem { - const fixedDenom = denom.startsWith("ibc/") - ? `${readDenom(denom).substring(0, 5)}...` + const tokenType = denom.startsWith("ibc/") + ? "ibc" : denom.startsWith("factory/") - ? denom.split("/").pop()?.slice(1).toUpperCase() - : readDenom(denom) + ? "factory" + : "" + const fixedDenom = + tokenType === "ibc" + ? `${readDenom(denom).substring(0, 5)}...` + : tokenType === "factory" && denom.split("/").length === 3 + ? denom.split("/").pop()?.slice(1).toUpperCase() + : tokenType === "factory" && denom.split("/").length > 3 + ? denom.split("/").slice(2)?.join(":").toUpperCase() + : readDenom(denom) let factoryIcon - if (denom.startsWith("factory/")) { + if (tokenType === "factory") { const chainID = getChainIDFromAddress(denom.split("/")[1], networks) if (chainID) { factoryIcon = networks[chainID].icon @@ -113,11 +121,12 @@ export const useNativeDenoms = () => { token: denom, symbol: fixedDenom, name: fixedDenom, - icon: denom.startsWith("ibc/") - ? "https://assets.terra.money/icon/svg/IBC.svg" - : denom.startsWith("factory/") - ? factoryIcon - : "https://assets.terra.money/icon/svg/Terra.svg", + icon: + tokenType === "ibc" + ? "https://assets.terra.money/icon/svg/IBC.svg" + : tokenType === "factory" + ? factoryIcon + : "https://assets.terra.money/icon/svg/Terra.svg", decimals: 6, } ) From f81f62dfb698d06e3dfe98b962cfc21d1d9d024a Mon Sep 17 00:00:00 2001 From: Michael Merz Date: Tue, 16 May 2023 19:53:54 -0400 Subject: [PATCH 09/25] feat: add gamm token lookup --- src/data/external/osmosis.ts | 67 ++++++++++++++++++++++++++++++++++++ src/data/query.ts | 1 + src/data/token.tsx | 40 ++++++++++++++------- 3 files changed, 96 insertions(+), 12 deletions(-) create mode 100644 src/data/external/osmosis.ts diff --git a/src/data/external/osmosis.ts b/src/data/external/osmosis.ts new file mode 100644 index 000000000..3f94b9c92 --- /dev/null +++ b/src/data/external/osmosis.ts @@ -0,0 +1,67 @@ +import { useQuery } from "react-query" +import request from "axios" +import { queryKey } from "../query" + +// https://api-osmosis.imperator.co/swagger/ +export const OSMOSIS_API_URL = "https://api-osmosis.imperator.co" + +interface IOsmosisPoolAsset { + symbol: string + amount: number + denom: string + coingecko_id: string + liquidity: number + liquidity_24h_change: number + volume_24h: number + volume_24h_change: number + volume_7d: number + price: number + fees: string +} + +interface IOsmosisPoolResponse { + // pool_id: pool asset array + [key: string]: IOsmosisPoolAsset[] +} + +/** + * Map token name to gamm denoms + * e.g. gamm/pool/1 -> ATOM-OSMO LP + * + * @returns a map of token name strings indexed by gamm denom + */ +export const useGammTokens = () => { + const fetch = useQuery( + [queryKey.gammTokens], + async () => { + try { + const { data } = await request.get( + "/pools/v2/all?low_liquidity=true", + { baseURL: OSMOSIS_API_URL } + ) + return data + } catch (error) { + console.error(error) + return + } + }, + { + // Data will never become stale and always stay in cache + cacheTime: Infinity, + staleTime: Infinity, + } + ) + + const gammTokens = new Map() + + if (fetch.data) { + for (const [poolId, poolAsset] of Object.entries(fetch.data)) { + gammTokens.set( + "gamm/pool/" + poolId, + poolAsset.map((asset) => asset.symbol).join("-") + " LP" + ) + } + } + + return gammTokens +} diff --git a/src/data/query.ts b/src/data/query.ts index 849ea9b5a..41aa1df83 100644 --- a/src/data/query.ts +++ b/src/data/query.ts @@ -35,6 +35,7 @@ export const queryKey = mirror({ TerraAssets: "", TerraAPI: "", History: "", + gammTokens: "", /* lcd */ alliance: { alliances: "", delegations: "", delegation: "" }, diff --git a/src/data/token.tsx b/src/data/token.tsx index eb5a9bacf..8706a1e90 100644 --- a/src/data/token.tsx +++ b/src/data/token.tsx @@ -5,6 +5,7 @@ import { AccAddress } from "@terra-money/feather.js" import { ASSETS } from "config/constants" import { useTokenInfoCW20 } from "./queries/wasm" import { useCustomTokensCW20 } from "./settings/CustomTokens" +import { useGammTokens } from "./external/osmosis" import { useCW20Whitelist, useIBCWhitelist } from "./Terra/TerraAssets" import { useWhitelist } from "./queries/chains" import { useNetworkName, useNetwork } from "./wallet" @@ -75,21 +76,36 @@ export const useNativeDenoms = () => { const { list: cw20 } = useCustomTokensCW20() const networkName = useNetworkName() const networks = useNetwork() + const gammTokens = useGammTokens() function readNativeDenom(denom: Denom): TokenItem { - const tokenType = denom.startsWith("ibc/") - ? "ibc" - : denom.startsWith("factory/") - ? "factory" - : "" - const fixedDenom = - tokenType === "ibc" - ? `${readDenom(denom).substring(0, 5)}...` - : tokenType === "factory" && denom.split("/").length === 3 - ? denom.split("/").pop()?.slice(1).toUpperCase() - : tokenType === "factory" && denom.split("/").length > 3 - ? denom.split("/").slice(2)?.join(":").toUpperCase() + let tokenType = "" + + if (denom.startsWith("ibc/")) { + tokenType = "ibc" + } else if (denom.startsWith("factory/")) { + tokenType = "factory" + } else if (denom.startsWith("gamm/")) { + tokenType = "gamm" + } + + let fixedDenom + if (tokenType === "ibc") { + fixedDenom = `${readDenom(denom).substring(0, 5)}...` + } else if (tokenType === "factory") { + const denomParts = denom.split("/") + if (denomParts.length === 3) { + fixedDenom = denomParts.pop()?.slice(1).toUpperCase() + } else if (denomParts.length > 3) { + fixedDenom = denomParts.slice(2)?.join(":").toUpperCase() + } + } else if (tokenType === "gamm") { + fixedDenom = gammTokens.get(denom) + ? gammTokens.get(denom) : readDenom(denom) + } else { + fixedDenom = readDenom(denom) + } let factoryIcon if (tokenType === "factory") { From d05a2fb7d0b98f91da6b67e62e49d536bd67e146 Mon Sep 17 00:00:00 2001 From: Michael Merz Date: Tue, 16 May 2023 23:10:31 -0400 Subject: [PATCH 10/25] chore: refactor token type if-else statement --- src/data/token.tsx | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/src/data/token.tsx b/src/data/token.tsx index 8706a1e90..f6abd1c09 100644 --- a/src/data/token.tsx +++ b/src/data/token.tsx @@ -90,21 +90,28 @@ export const useNativeDenoms = () => { } let fixedDenom - if (tokenType === "ibc") { - fixedDenom = `${readDenom(denom).substring(0, 5)}...` - } else if (tokenType === "factory") { - const denomParts = denom.split("/") - if (denomParts.length === 3) { - fixedDenom = denomParts.pop()?.slice(1).toUpperCase() - } else if (denomParts.length > 3) { - fixedDenom = denomParts.slice(2)?.join(":").toUpperCase() + + switch (tokenType) { + case "ibc": + fixedDenom = `${readDenom(denom).substring(0, 5)}...` + break + + case "factory": { + const denomParts = denom.split("/") + if (denomParts.length === 3) { + fixedDenom = denomParts.pop()?.slice(1).toUpperCase() + } else if (denomParts.length > 3) { + fixedDenom = denomParts.slice(2)?.join(":").toUpperCase() + } + break } - } else if (tokenType === "gamm") { - fixedDenom = gammTokens.get(denom) - ? gammTokens.get(denom) - : readDenom(denom) - } else { - fixedDenom = readDenom(denom) + + case "gamm": + fixedDenom = gammTokens.get(denom) ?? readDenom(denom) + break + + default: + fixedDenom = readDenom(denom) } let factoryIcon From 03f2d21e032204706ec7175194dd075090d82c5b Mon Sep 17 00:00:00 2001 From: Michael Merz Date: Wed, 17 May 2023 09:11:26 -0400 Subject: [PATCH 11/25] feat: add gamm token decimals. add gamm to "hide low balance" --- src/data/external/osmosis.ts | 2 ++ src/data/token.tsx | 9 +++++++-- src/utils/chain.ts | 1 + 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/data/external/osmosis.ts b/src/data/external/osmosis.ts index 3f94b9c92..cd6164225 100644 --- a/src/data/external/osmosis.ts +++ b/src/data/external/osmosis.ts @@ -5,6 +5,8 @@ import { queryKey } from "../query" // https://api-osmosis.imperator.co/swagger/ export const OSMOSIS_API_URL = "https://api-osmosis.imperator.co" +export const GAMM_TOKEN_DECIMALS = 18 + interface IOsmosisPoolAsset { symbol: string amount: number diff --git a/src/data/token.tsx b/src/data/token.tsx index f6abd1c09..369a54423 100644 --- a/src/data/token.tsx +++ b/src/data/token.tsx @@ -5,12 +5,14 @@ import { AccAddress } from "@terra-money/feather.js" import { ASSETS } from "config/constants" import { useTokenInfoCW20 } from "./queries/wasm" import { useCustomTokensCW20 } from "./settings/CustomTokens" -import { useGammTokens } from "./external/osmosis" +import { useGammTokens, GAMM_TOKEN_DECIMALS } from "./external/osmosis" import { useCW20Whitelist, useIBCWhitelist } from "./Terra/TerraAssets" import { useWhitelist } from "./queries/chains" import { useNetworkName, useNetwork } from "./wallet" import { getChainIDFromAddress } from "utils/bech32" +const DEFAULT_NATIVE_DECIMALS = 6 + export const useTokenItem = (token: Token): TokenItem | undefined => { const readNativeDenom = useNativeDenoms() @@ -78,6 +80,8 @@ export const useNativeDenoms = () => { const networks = useNetwork() const gammTokens = useGammTokens() + let decimals = DEFAULT_NATIVE_DECIMALS + function readNativeDenom(denom: Denom): TokenItem { let tokenType = "" @@ -87,6 +91,7 @@ export const useNativeDenoms = () => { tokenType = "factory" } else if (denom.startsWith("gamm/")) { tokenType = "gamm" + decimals = GAMM_TOKEN_DECIMALS } let fixedDenom @@ -150,7 +155,7 @@ export const useNativeDenoms = () => { : tokenType === "factory" ? factoryIcon : "https://assets.terra.money/icon/svg/Terra.svg", - decimals: 6, + decimals, } ) } diff --git a/src/utils/chain.ts b/src/utils/chain.ts index 8cab487c1..8112a0776 100644 --- a/src/utils/chain.ts +++ b/src/utils/chain.ts @@ -60,4 +60,5 @@ export const useTerraChainName = () => export const isNativeToken = (denom: string) => !denom.startsWith("ibc/") && !denom.startsWith("factory/") && + !denom.startsWith("gamm/") && !AccAddress.validate(denom) From 077ed17fb3ed6a9fa36102f481c91e3e9fcd9bce Mon Sep 17 00:00:00 2001 From: Michael Merz Date: Wed, 17 May 2023 13:05:35 -0400 Subject: [PATCH 12/25] feat: truncate long bank token names & stub factory tokens pending whitelist --- src/data/external/osmosis.ts | 2 ++ src/data/token.tsx | 31 ++++++++++++++++++------------- src/pages/wallet/Asset.tsx | 10 ++++++++-- src/pages/wallet/AssetChain.tsx | 8 +++++++- 4 files changed, 35 insertions(+), 16 deletions(-) diff --git a/src/data/external/osmosis.ts b/src/data/external/osmosis.ts index cd6164225..031773625 100644 --- a/src/data/external/osmosis.ts +++ b/src/data/external/osmosis.ts @@ -6,6 +6,8 @@ import { queryKey } from "../query" export const OSMOSIS_API_URL = "https://api-osmosis.imperator.co" export const GAMM_TOKEN_DECIMALS = 18 +export const OSMO_ICON = + "https://raw.githubusercontent.com/terra-money/station-assets/main/img/chains/Osmosis.svg" interface IOsmosisPoolAsset { symbol: string diff --git a/src/data/token.tsx b/src/data/token.tsx index 369a54423..dbb1f101c 100644 --- a/src/data/token.tsx +++ b/src/data/token.tsx @@ -5,7 +5,11 @@ import { AccAddress } from "@terra-money/feather.js" import { ASSETS } from "config/constants" import { useTokenInfoCW20 } from "./queries/wasm" import { useCustomTokensCW20 } from "./settings/CustomTokens" -import { useGammTokens, GAMM_TOKEN_DECIMALS } from "./external/osmosis" +import { + useGammTokens, + GAMM_TOKEN_DECIMALS, + OSMO_ICON, +} from "./external/osmosis" import { useCW20Whitelist, useIBCWhitelist } from "./Terra/TerraAssets" import { useWhitelist } from "./queries/chains" import { useNetworkName, useNetwork } from "./wallet" @@ -94,27 +98,23 @@ export const useNativeDenoms = () => { decimals = GAMM_TOKEN_DECIMALS } - let fixedDenom + let fixedDenom = "" switch (tokenType) { case "ibc": fixedDenom = `${readDenom(denom).substring(0, 5)}...` break - case "factory": { - const denomParts = denom.split("/") - if (denomParts.length === 3) { - fixedDenom = denomParts.pop()?.slice(1).toUpperCase() - } else if (denomParts.length > 3) { - fixedDenom = denomParts.slice(2)?.join(":").toUpperCase() - } - break - } - case "gamm": fixedDenom = gammTokens.get(denom) ?? readDenom(denom) break + case "factory": { + // TODO - lookup whitelist for factory tokens. + fixedDenom = "..." + break + } + default: fixedDenom = readDenom(denom) } @@ -126,6 +126,11 @@ export const useNativeDenoms = () => { factoryIcon = networks[chainID].icon } } + + if (tokenType === "gamm") { + factoryIcon = OSMO_ICON + } + // native token if (whitelist[networkName]?.[denom]) return whitelist[networkName]?.[denom] @@ -152,7 +157,7 @@ export const useNativeDenoms = () => { icon: tokenType === "ibc" ? "https://assets.terra.money/icon/svg/IBC.svg" - : tokenType === "factory" + : tokenType === "factory" || tokenType === "gamm" ? factoryIcon : "https://assets.terra.money/icon/svg/Terra.svg", decimals, diff --git a/src/pages/wallet/Asset.tsx b/src/pages/wallet/Asset.tsx index 3749023df..b189f885a 100644 --- a/src/pages/wallet/Asset.tsx +++ b/src/pages/wallet/Asset.tsx @@ -48,7 +48,7 @@ const Asset = (props: Props) => {

- {symbol} + {symbol.length > 16 ? symbol.slice(0, 13) + "..." : symbol} {chains.map((chain) => ( {network[chain].name || chain} @@ -86,7 +86,13 @@ const Asset = (props: Props) => { {t("Failed to query balance")} ) : ( - + )} )} diff --git a/src/pages/wallet/AssetChain.tsx b/src/pages/wallet/AssetChain.tsx index 39749d2d8..ee6051e33 100644 --- a/src/pages/wallet/AssetChain.tsx +++ b/src/pages/wallet/AssetChain.tsx @@ -54,7 +54,13 @@ const AssetChain = (props: Props) => { {wrong ? ( {t("Failed to query balance")} ) : ( - + )} )} From fc7f905a6e6729e97baaba251ab60f527a567b25 Mon Sep 17 00:00:00 2001 From: Manuel Alessandro Collazo Date: Mon, 22 May 2023 15:54:45 +0100 Subject: [PATCH 13/25] fix: token address parsing factory tokens may have addresses split by either `/` or `:` characters. --- src/data/token.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/data/token.tsx b/src/data/token.tsx index dbb1f101c..e64a4fea2 100644 --- a/src/data/token.tsx +++ b/src/data/token.tsx @@ -121,7 +121,8 @@ export const useNativeDenoms = () => { let factoryIcon if (tokenType === "factory") { - const chainID = getChainIDFromAddress(denom.split("/")[1], networks) + const tokenAddress = denom.split(/[/:]/)[1] + const chainID = getChainIDFromAddress(tokenAddress, networks) if (chainID) { factoryIcon = networks[chainID].icon } From e82ac8d0973f0ed7b51f6d98d3c11619730b825d Mon Sep 17 00:00:00 2001 From: plubber <51789398+ericHgorski@users.noreply.github.com> Date: Thu, 11 May 2023 11:27:55 -0400 Subject: [PATCH 14/25] fix: always show native assets regardless of denoms (#348) * St 353 display chain selection on inactive chain click in OtherChainButtons (#329) * WIP: chains display prefs open * feat: open preferences on click * cleanup * cleanup * feat: swap terra.kitchen/utils for @terra-money/terra-utils (#321) * feat: guard against empty networks, chaindID, or lcd (#327) * feat: kado integration and updated wallet buttons (#320) * feat: kado integration and updated wallet buttons * Fix passing/not passing * fix prettier * fix: always show native assets regardless of denoms * Update chain.ts * fix: hide low balance * fix: remove log * fix: code readability Changing code to follow logic for improved readability. * fix: exclude cw20 tokens --------- Co-authored-by: Mike <17463738+mwmerz@users.noreply.github.com> Co-authored-by: Alessandro Candeago <54709706+alecande11@users.noreply.github.com> Co-authored-by: Manuel Alessandro Collazo --- src/pages/wallet/AssetList.module.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/wallet/AssetList.module.scss b/src/pages/wallet/AssetList.module.scss index 7ef17312d..ff8868cc6 100644 --- a/src/pages/wallet/AssetList.module.scss +++ b/src/pages/wallet/AssetList.module.scss @@ -4,6 +4,7 @@ background-color: var(--card-bg-muted); padding-inline: 10px; grid-area: list; + display: grid; grid-template-areas: "title" "list"; grid-template-rows: min-content auto; From 00b710843280961fc9fa7a301d885a1492b5715d Mon Sep 17 00:00:00 2001 From: Joshua Brigati Date: Tue, 16 May 2023 10:15:14 -0500 Subject: [PATCH 15/25] St 355 moon theme (#379) * changes blossom theme colors and changed some font styles mainly font-smoothing and font weight adjusted to reflect antialiased smoothing * Added navigation background and aside blurs ST-309 * changed blossom mp4 location * fixed aside blur on smaller heights * changed where the aside blurs are placed and added better management for mobile and small heights * fixed other changes I wanted in * changes colors * completed moon theme changes. Also implimented two toned cards the are on all themes along with some other small UI changes ST-355 * fixing mobile issues ST-355 * added theme stake amounts back in * changed background blob color to lighter, changed preview svg and removed staking minimums to themes * removed image I forgot to delete --- src/pages/wallet/AssetList.module.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/wallet/AssetList.module.scss b/src/pages/wallet/AssetList.module.scss index ff8868cc6..7ef17312d 100644 --- a/src/pages/wallet/AssetList.module.scss +++ b/src/pages/wallet/AssetList.module.scss @@ -4,7 +4,6 @@ background-color: var(--card-bg-muted); padding-inline: 10px; grid-area: list; - display: grid; grid-template-areas: "title" "list"; grid-template-rows: min-content auto; From b507c4fd2bca8b9958ce0f222859561a55c1d655 Mon Sep 17 00:00:00 2001 From: Manuel Alessandro Collazo Date: Mon, 29 May 2023 16:22:35 +0100 Subject: [PATCH 16/25] fix: update osmo icon link --- src/data/external/osmosis.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/external/osmosis.ts b/src/data/external/osmosis.ts index 031773625..c10bc60a9 100644 --- a/src/data/external/osmosis.ts +++ b/src/data/external/osmosis.ts @@ -7,7 +7,7 @@ export const OSMOSIS_API_URL = "https://api-osmosis.imperator.co" export const GAMM_TOKEN_DECIMALS = 18 export const OSMO_ICON = - "https://raw.githubusercontent.com/terra-money/station-assets/main/img/chains/Osmosis.svg" + "https://station-assets.terra.money/img/chains/Osmosis.svg" interface IOsmosisPoolAsset { symbol: string From 9f4d68c1f4d7223086afdef9fdcd3ee49490e2d6 Mon Sep 17 00:00:00 2001 From: Joshua Date: Wed, 24 May 2023 12:56:46 -0500 Subject: [PATCH 17/25] removed Medium and changed Github link ST-452 --- src/app/sections/Links.tsx | 3 +-- src/components/layout/Contacts.tsx | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/app/sections/Links.tsx b/src/app/sections/Links.tsx index 3e1ae91bb..3676cc659 100644 --- a/src/app/sections/Links.tsx +++ b/src/app/sections/Links.tsx @@ -15,11 +15,10 @@ const Links = () => { const { name } = useTheme() const community = { - medium: "https://medium.com/terra-money", discord: "https://terra.sc/discord", telegram: "https://t.me/TerraNetworkLobby", twitter: "https://twitter.com/terra_money", - github: "https://github.com/terra-money", + github: "https://github.com/terra-money/station", } return ( diff --git a/src/components/layout/Contacts.tsx b/src/components/layout/Contacts.tsx index 8bb12f6d5..388153a9e 100644 --- a/src/components/layout/Contacts.tsx +++ b/src/components/layout/Contacts.tsx @@ -2,7 +2,6 @@ import { ReactNode } from "react" import classNames from "classnames/bind" import { useTheme } from "data/settings/Theme" import { Contacts as ContactProps } from "types/components" -import { ReactComponent as Medium } from "styles/images/community/Medium.svg" import { ReactComponent as Discord } from "styles/images/community/Discord.svg" import { ReactComponent as Telegram } from "styles/images/community/Telegram.svg" import { ReactComponent as Twitter } from "styles/images/community/Twitter.svg" @@ -16,7 +15,6 @@ const cx = classNames.bind(styles) const ICON_SIZE = { width: 18, height: 18 } const icons: Record = { - medium: , discord: , telegram: , twitter: , From 4726f423b6ff9a76085d01bf29c1f7ffbf1aec5d Mon Sep 17 00:00:00 2001 From: Joshua Date: Mon, 29 May 2023 10:31:17 -0500 Subject: [PATCH 18/25] deleted Medium svg because its no longer used --- src/styles/images/community/Medium.svg | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 src/styles/images/community/Medium.svg diff --git a/src/styles/images/community/Medium.svg b/src/styles/images/community/Medium.svg deleted file mode 100644 index 78af371b4..000000000 --- a/src/styles/images/community/Medium.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - From ef4340bb935a83401b5c29d4a7a2c74a40887b0f Mon Sep 17 00:00:00 2001 From: Mike <17463738+mwmerz@users.noreply.github.com> Date: Tue, 30 May 2023 08:46:22 -0400 Subject: [PATCH 19/25] St-440-implement-sentry (#395) * feat: add sentry support --- package-lock.json | 233 +++++++++++++++++++++++++++++++- package.json | 3 +- src/index.tsx | 2 + src/utils/sentry/setupSentry.ts | 16 +++ 4 files changed, 247 insertions(+), 7 deletions(-) create mode 100644 src/utils/sentry/setupSentry.ts diff --git a/package-lock.json b/package-lock.json index c137cf6f7..f5bfe79e0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@ledgerhq/hw-transport-web-ble": "^6.27.1", "@mui/icons-material": "^5.8.0", "@mui/material": "^5.9.1", + "@sentry/react": "^7.53.1", "@terra-money/feather.js": "^1.0.4", "@terra-money/ledger-station-js": "^1.3.7", "@terra-money/log-finder-ruleset": "^3.0.0", @@ -3670,6 +3671,125 @@ "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", "dev": true }, + "node_modules/@sentry-internal/tracing": { + "version": "7.53.1", + "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.53.1.tgz", + "integrity": "sha512-a4H4rvVdz0XDGgNfRqc7zg6rMt2P1P05xBmgfIfztYy94Vciw1QMdboNiT7einr8ra8wogdEaK4Pe2AzYAPBJQ==", + "dependencies": { + "@sentry/core": "7.53.1", + "@sentry/types": "7.53.1", + "@sentry/utils": "7.53.1", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@sentry-internal/tracing/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@sentry/browser": { + "version": "7.53.1", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.53.1.tgz", + "integrity": "sha512-1zas2R6riJaj0k7FoeieCW0SuC7UyKaBGA6jEG2LsgIqyD7IDOlF3BPZ4Yt08GFav0ImpyhGn5Vbrq5JLbeQdw==", + "dependencies": { + "@sentry-internal/tracing": "7.53.1", + "@sentry/core": "7.53.1", + "@sentry/replay": "7.53.1", + "@sentry/types": "7.53.1", + "@sentry/utils": "7.53.1", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@sentry/browser/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@sentry/core": { + "version": "7.53.1", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.53.1.tgz", + "integrity": "sha512-DAH8IJNORJJ7kQLqsZuhMkN6cwJjXzFuuUoZor7IIDHIHjtl51W+2F3Stg3+I3ZoKDfJfUNKqhipk2WZjG0FBg==", + "dependencies": { + "@sentry/types": "7.53.1", + "@sentry/utils": "7.53.1", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@sentry/core/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@sentry/react": { + "version": "7.53.1", + "resolved": "https://registry.npmjs.org/@sentry/react/-/react-7.53.1.tgz", + "integrity": "sha512-eEOY/peBepSD/nhPn4SU77aYdjQfAI1svOqpG4sbpjaGZU1P6L7+IIGmip8l2T68oPEeKDaiH9Qy/3uxu55B/Q==", + "dependencies": { + "@sentry/browser": "7.53.1", + "@sentry/types": "7.53.1", + "@sentry/utils": "7.53.1", + "hoist-non-react-statics": "^3.3.2", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "react": "15.x || 16.x || 17.x || 18.x" + } + }, + "node_modules/@sentry/react/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@sentry/replay": { + "version": "7.53.1", + "resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.53.1.tgz", + "integrity": "sha512-5He5JLJiYLeWtXHC53z2ZzfbgAedafbHNZVS4+MBCOtydCk7cnuyJ0gGV6Rfxej/lZSNXZxOdW7HeMhzBtZCxw==", + "dependencies": { + "@sentry/core": "7.53.1", + "@sentry/types": "7.53.1", + "@sentry/utils": "7.53.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@sentry/types": { + "version": "7.53.1", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.53.1.tgz", + "integrity": "sha512-/ijchRIu+jz3+j/zY+7KRPfLSCY14fTx5xujjbOdmEKjmIHQmwPBdszcQm40uwofrR8taV4hbt5MFN+WnjCkCw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@sentry/utils": { + "version": "7.53.1", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.53.1.tgz", + "integrity": "sha512-DKJA1LSUOEv4KOR828MzVuLh+drjeAgzyKgN063OEKmnirgjgRgNNS8wUgwpG0Tn2k6ANZGCwrdfzPeSBxshKg==", + "dependencies": { + "@sentry/types": "7.53.1", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@sentry/utils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, "node_modules/@sinonjs/commons": { "version": "1.8.3", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", @@ -12156,7 +12276,6 @@ "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "devOptional": true, "dependencies": { "react-is": "^16.7.0" } @@ -12164,8 +12283,7 @@ "node_modules/hoist-non-react-statics/node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "devOptional": true + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "node_modules/hoopy": { "version": "0.1.4", @@ -30005,6 +30123,111 @@ } } }, + "@sentry-internal/tracing": { + "version": "7.53.1", + "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.53.1.tgz", + "integrity": "sha512-a4H4rvVdz0XDGgNfRqc7zg6rMt2P1P05xBmgfIfztYy94Vciw1QMdboNiT7einr8ra8wogdEaK4Pe2AzYAPBJQ==", + "requires": { + "@sentry/core": "7.53.1", + "@sentry/types": "7.53.1", + "@sentry/utils": "7.53.1", + "tslib": "^1.9.3" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@sentry/browser": { + "version": "7.53.1", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.53.1.tgz", + "integrity": "sha512-1zas2R6riJaj0k7FoeieCW0SuC7UyKaBGA6jEG2LsgIqyD7IDOlF3BPZ4Yt08GFav0ImpyhGn5Vbrq5JLbeQdw==", + "requires": { + "@sentry-internal/tracing": "7.53.1", + "@sentry/core": "7.53.1", + "@sentry/replay": "7.53.1", + "@sentry/types": "7.53.1", + "@sentry/utils": "7.53.1", + "tslib": "^1.9.3" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@sentry/core": { + "version": "7.53.1", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.53.1.tgz", + "integrity": "sha512-DAH8IJNORJJ7kQLqsZuhMkN6cwJjXzFuuUoZor7IIDHIHjtl51W+2F3Stg3+I3ZoKDfJfUNKqhipk2WZjG0FBg==", + "requires": { + "@sentry/types": "7.53.1", + "@sentry/utils": "7.53.1", + "tslib": "^1.9.3" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@sentry/react": { + "version": "7.53.1", + "resolved": "https://registry.npmjs.org/@sentry/react/-/react-7.53.1.tgz", + "integrity": "sha512-eEOY/peBepSD/nhPn4SU77aYdjQfAI1svOqpG4sbpjaGZU1P6L7+IIGmip8l2T68oPEeKDaiH9Qy/3uxu55B/Q==", + "requires": { + "@sentry/browser": "7.53.1", + "@sentry/types": "7.53.1", + "@sentry/utils": "7.53.1", + "hoist-non-react-statics": "^3.3.2", + "tslib": "^1.9.3" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@sentry/replay": { + "version": "7.53.1", + "resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.53.1.tgz", + "integrity": "sha512-5He5JLJiYLeWtXHC53z2ZzfbgAedafbHNZVS4+MBCOtydCk7cnuyJ0gGV6Rfxej/lZSNXZxOdW7HeMhzBtZCxw==", + "requires": { + "@sentry/core": "7.53.1", + "@sentry/types": "7.53.1", + "@sentry/utils": "7.53.1" + } + }, + "@sentry/types": { + "version": "7.53.1", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.53.1.tgz", + "integrity": "sha512-/ijchRIu+jz3+j/zY+7KRPfLSCY14fTx5xujjbOdmEKjmIHQmwPBdszcQm40uwofrR8taV4hbt5MFN+WnjCkCw==" + }, + "@sentry/utils": { + "version": "7.53.1", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.53.1.tgz", + "integrity": "sha512-DKJA1LSUOEv4KOR828MzVuLh+drjeAgzyKgN063OEKmnirgjgRgNNS8wUgwpG0Tn2k6ANZGCwrdfzPeSBxshKg==", + "requires": { + "@sentry/types": "7.53.1", + "tslib": "^1.9.3" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, "@sinonjs/commons": { "version": "1.8.3", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", @@ -36872,7 +37095,6 @@ "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "devOptional": true, "requires": { "react-is": "^16.7.0" }, @@ -36880,8 +37102,7 @@ "react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "devOptional": true + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" } } }, diff --git a/package.json b/package.json index 50e7a9824..15671f899 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "@ledgerhq/hw-transport-web-ble": "^6.27.1", "@mui/icons-material": "^5.8.0", "@mui/material": "^5.9.1", + "@sentry/react": "^7.53.1", "@terra-money/feather.js": "^1.0.4", "@terra-money/ledger-station-js": "^1.3.7", "@terra-money/log-finder-ruleset": "^3.0.0", @@ -25,8 +26,8 @@ "@terra-money/terra.js": "^3.1.7", "@terra-money/terra.proto": "^2.0.0", "@terra-money/use-wallet": "4.0.0-beta.3", - "@terra-money/wallet-provider": "4.0.0-beta.3", "@terra-money/wallet-controller": "4.0.0-beta.3", + "@terra-money/wallet-provider": "4.0.0-beta.3", "@tippyjs/react": "^4.2.6", "axios": "^0.27.2", "bech32": "^2.0.0", diff --git a/src/index.tsx b/src/index.tsx index aba23dbd7..0e2083efc 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -6,6 +6,7 @@ import { RecoilRoot } from "recoil" import { getChainOptions } from "@terra-money/wallet-controller" import { WalletProvider } from "@terra-money/wallet-provider" import "tippy.js/dist/tippy.css" +import { initSentry } from "utils/sentry/setupSentry" import "config/lang" import { BRIDGE } from "config/constants" @@ -23,6 +24,7 @@ import WithNodeInfo from "app/WithNodeInfo" import InitQueryClient from "app/InitQueryClient" const connectorOpts = { bridge: BRIDGE } +initSentry() getChainOptions().then((chainOptions) => render( diff --git a/src/utils/sentry/setupSentry.ts b/src/utils/sentry/setupSentry.ts new file mode 100644 index 000000000..25832970d --- /dev/null +++ b/src/utils/sentry/setupSentry.ts @@ -0,0 +1,16 @@ +import * as Sentry from "@sentry/react" + +const SENTRY_DSN = process.env.REACT_APP_SENTRY_DSN +const ENVIRONMENT = process.env.NODE_ENV + +export function initSentry() { + Sentry.init({ + dsn: SENTRY_DSN, + integrations: [new Sentry.BrowserTracing(), new Sentry.Replay()], + // Performance Monitoring + tracesSampleRate: 1.0, // Capture 100% of the transactions, reduce in production! + // Session Replay + replaysSessionSampleRate: ENVIRONMENT === "development" ? 1.0 : 0.1, // This sets the sample rate at 10%. You may want to change it to 100% while in development and then sample at a lower rate in production. + replaysOnErrorSampleRate: 1.0, // If you're not already sampling the entire session, change the sample rate to 100% when sampling sessions where errors occur. + }) +} From ac3a9ae259a6b264a5881f2b8bd4fff7eb952be7 Mon Sep 17 00:00:00 2001 From: Alessandro Candeago <54709706+alecande11@users.noreply.github.com> Date: Tue, 30 May 2023 17:46:59 +0200 Subject: [PATCH 20/25] Resolve unknown IBC tokens (#400) --- src/app/InitBankBalance.tsx | 16 +- src/app/InitChains.tsx | 4 +- src/data/queries/ibc.ts | 103 +++++- src/data/token.tsx | 26 +- src/pages/wallet/Asset.tsx | 6 +- src/pages/wallet/AssetChain.module.scss | 30 +- src/pages/wallet/AssetChain.tsx | 66 +++- src/pages/wallet/AssetList.tsx | 85 ++++- src/pages/wallet/AssetPage.tsx | 107 +++++-- src/pages/wallet/Coins.tsx | 9 +- src/pages/wallet/IbcSendBack.tsx | 25 ++ src/pages/wallet/IbcSendBackTx.module.scss | 63 ++++ src/pages/wallet/IbcSendBackTx.tsx | 347 +++++++++++++++++++++ src/pages/wallet/SendPage.tsx | 12 +- src/txs/Tx.tsx | 47 ++- 15 files changed, 855 insertions(+), 91 deletions(-) create mode 100644 src/pages/wallet/IbcSendBack.tsx create mode 100644 src/pages/wallet/IbcSendBackTx.module.scss create mode 100644 src/pages/wallet/IbcSendBackTx.tsx diff --git a/src/app/InitBankBalance.tsx b/src/app/InitBankBalance.tsx index 9c4976889..d8082cb0a 100644 --- a/src/app/InitBankBalance.tsx +++ b/src/app/InitBankBalance.tsx @@ -5,19 +5,19 @@ import { useInitialTokenBalance, } from "data/queries/bank" import { BankBalanceProvider } from "data/queries/bank" -import { useNetworkName } from "data/wallet" +//import { useNetworkName } from "data/wallet" import { combineState } from "data/query" import { WithFetching } from "components/feedback" -import { useCustomTokensNative } from "data/settings/CustomTokens" -import { useWhitelist } from "data/queries/chains" +//import { useCustomTokensNative } from "data/settings/CustomTokens" +//import { useWhitelist } from "data/queries/chains" const InitBankBalance = ({ children }: PropsWithChildren<{}>) => { const balances = useInitialBankBalance() const tokenBalancesQuery = useInitialTokenBalance() - const native = useCustomTokensNative() - const { whitelist } = useWhitelist() + //const native = useCustomTokensNative() + //const { whitelist } = useWhitelist() - const networkName = useNetworkName() + //const networkName = useNetworkName() const state = combineState(...balances, ...tokenBalancesQuery) const bankBalance = balances.reduce( @@ -29,7 +29,7 @@ const InitBankBalance = ({ children }: PropsWithChildren<{}>) => { [] as CoinBalance[] ) - native.list.forEach(({ denom }) => { + /*native.list.forEach(({ denom }) => { if (!bankBalance.find((balance) => balance.denom === denom)) { const token = whitelist[networkName][denom] @@ -41,7 +41,7 @@ const InitBankBalance = ({ children }: PropsWithChildren<{}>) => { chain: token.chains[0], }) } - }) + })*/ return ( diff --git a/src/app/InitChains.tsx b/src/app/InitChains.tsx index fc12f1a0b..b3786f145 100644 --- a/src/app/InitChains.tsx +++ b/src/app/InitChains.tsx @@ -11,10 +11,10 @@ const InitChains = ({ children }: PropsWithChildren<{}>) => { useEffect(() => { axios - .get("/coins.json", { baseURL: STATION_ASSETS }) + .get("/denoms.json", { baseURL: STATION_ASSETS }) .then(({ data }) => setWhitelist(data)) axios - .get("/ibc_denoms.json", { baseURL: STATION_ASSETS }) + .get("/ibc.json", { baseURL: STATION_ASSETS }) .then(({ data }) => setIbcDenoms(data)) axios .get("/station/coins.json", { baseURL: ASSETS }) diff --git a/src/data/queries/ibc.ts b/src/data/queries/ibc.ts index 8e1b01184..15fb65c47 100644 --- a/src/data/queries/ibc.ts +++ b/src/data/queries/ibc.ts @@ -1,7 +1,10 @@ -import { useQuery } from "react-query" +import { useQueries, useQuery } from "react-query" import { isDenomIBC } from "@terra-money/terra-utils" import { queryKey, RefetchOptions } from "../query" import { useInterchainLCDClient } from "./lcdClient" +import { useNetwork } from "data/wallet" +import axios from "axios" +import crypto from "crypto" export const useIBCBaseDenom = ( denom: Denom, @@ -9,17 +12,107 @@ export const useIBCBaseDenom = ( enabled: boolean ) => { const lcd = useInterchainLCDClient() + const network = useNetwork() return useQuery( - [queryKey.ibc.denomTrace, denom], + [queryKey.ibc.denomTrace, denom, chainID], async () => { - const { base_denom } = await lcd.ibcTransfer.denomTrace( + const { base_denom, path } = await lcd.ibcTransfer.denomTrace( denom.replace("ibc/", ""), chainID ) - return base_denom + const paths = path.split("/") + const chains = [chainID] + const channels = [] + + for (let i = 0; i < paths.length; i += 2) { + const chain = chains[chains.length - 1] + + if (!network[chain]?.lcd) return + + const [port, channel] = [paths[i], paths[i + 1]] + channels.unshift({ port, channel }) + + const { data } = await axios.get( + `/ibc/core/channel/v1/channels/${channel}/ports/${port}/client_state`, + { baseURL: network[chain].lcd } + ) + + chains.unshift(data.identified_client_state.client_state.chain_id) + } + + return { + ibcDenom: denom, + baseDenom: base_denom.startsWith("cw20:") + ? base_denom.replace("cw20:", "") + : base_denom, + chainIDs: chains, + channels, + } }, - { ...RefetchOptions.INFINITY, enabled: isDenomIBC(denom) && enabled } + { + ...RefetchOptions.INFINITY, + enabled: isDenomIBC(denom) && !!network[chainID] && enabled, + } ) } + +export const useIBCBaseDenoms = (data: { denom: Denom; chainID: string }[]) => { + const network = useNetwork() + const lcd = useInterchainLCDClient() + + return useQueries( + data.map(({ denom, chainID }) => { + return { + queryKey: [queryKey.ibc.denomTrace, denom, network], + queryFn: async () => { + const { base_denom, path } = await lcd.ibcTransfer.denomTrace( + denom.replace("ibc/", ""), + chainID + ) + + const paths = path.split("/") + const chains = [chainID] + const channels = [] + + for (let i = 0; i < paths.length; i += 2) { + const chain = chains[chains.length - 1] + + if (!network[chain]?.lcd) return + + const [port, channel] = [paths[i], paths[i + 1]] + channels.unshift({ port, channel }) + + const { data } = await axios.get( + `/ibc/core/channel/v1/channels/${channel}/ports/${port}/client_state`, + { baseURL: network[chain].lcd } + ) + + chains.unshift(data.identified_client_state.client_state.chain_id) + } + + return { + ibcDenom: denom, + baseDenom: base_denom.startsWith("cw20:") + ? base_denom.replace("cw20:", "") + : base_denom, + chainIDs: chains, + channels, + } + }, + ...RefetchOptions.INFINITY, + enabled: isDenomIBC(denom) && !!network[chainID], + } + }) + ) +} + +export function calculateIBCDenom(baseDenom: string, path: string) { + if (!path) return baseDenom + + const assetString = [path, baseDenom].join("/") + const hash = crypto.createHash("sha256") + hash.update(assetString) + return `ibc/${hash.digest("hex").toUpperCase()}` +} diff --git a/src/data/token.tsx b/src/data/token.tsx index e64a4fea2..77b60a4dc 100644 --- a/src/data/token.tsx +++ b/src/data/token.tsx @@ -17,7 +17,10 @@ import { getChainIDFromAddress } from "utils/bech32" const DEFAULT_NATIVE_DECIMALS = 6 -export const useTokenItem = (token: Token): TokenItem | undefined => { +export const useTokenItem = ( + token: Token, + chainID?: string +): TokenItem | undefined => { const readNativeDenom = useNativeDenoms() /* CW20 */ @@ -61,17 +64,18 @@ export const useTokenItem = (token: Token): TokenItem | undefined => { return readIBCDenom(item) } - return readNativeDenom(token) + return readNativeDenom(token, chainID) } interface Props { token: Token + chainID?: string children: (token: TokenItem) => ReactNode } -export const WithTokenItem = ({ token, children }: Props) => { +export const WithTokenItem = ({ token, chainID, children }: Props) => { const readNativeDenom = useNativeDenoms() - return <>{children(readNativeDenom(token))} + return <>{children(readNativeDenom(token, chainID))} } /* helpers */ @@ -86,7 +90,7 @@ export const useNativeDenoms = () => { let decimals = DEFAULT_NATIVE_DECIMALS - function readNativeDenom(denom: Denom): TokenItem { + function readNativeDenom(denom: Denom, chainID?: string): TokenItem { let tokenType = "" if (denom.startsWith("ibc/")) { @@ -133,7 +137,17 @@ export const useNativeDenoms = () => { } // native token - if (whitelist[networkName]?.[denom]) return whitelist[networkName]?.[denom] + if (chainID) { + const tokenID = `${chainID}:${denom}` + + if (whitelist[networkName]?.[tokenID]) + return whitelist[networkName]?.[tokenID] + } else { + const tokenDetails = Object.values(whitelist[networkName] ?? {}).find( + ({ token }) => token === denom + ) + if (tokenDetails) return tokenDetails + } // ibc token const ibcToken = ibcDenoms[networkName]?.[denom]?.token diff --git a/src/pages/wallet/Asset.tsx b/src/pages/wallet/Asset.tsx index b189f885a..cb9a7f301 100644 --- a/src/pages/wallet/Asset.tsx +++ b/src/pages/wallet/Asset.tsx @@ -18,10 +18,11 @@ export interface Props extends TokenItem, QueryState { change?: number hideActions?: boolean chains: string[] + id: string } const Asset = (props: Props) => { - const { token, icon, symbol, balance, denom, decimals, ...state } = props + const { token, icon, symbol, balance, denom, decimals, id, ...state } = props const { t } = useTranslation() const currency = useCurrency() const network = useNetwork() @@ -38,9 +39,8 @@ const Asset = (props: Props) => { return (
- setRoute({ path: Path.coin, denom: token, previousPage: route }) + setRoute({ path: Path.coin, denom: id, previousPage: route }) } >
diff --git a/src/pages/wallet/AssetChain.module.scss b/src/pages/wallet/AssetChain.module.scss index efbac1616..a80f85c82 100644 --- a/src/pages/wallet/AssetChain.module.scss +++ b/src/pages/wallet/AssetChain.module.scss @@ -25,8 +25,36 @@ .name { grid-area: name; - @include flex(flex-start, center); + @include flex(center, flex-start); + flex-direction: column; font-size: 15px; + + p { + font-size: 13px; + font-weight: var(--normal); + color: var(--text-muted); + white-space: nowrap; + } + + h4 { + @include flex(flex-start); + gap: 8px; + + .send__back__button { + color: var(--warning); + &:hover { + text-decoration: underline; + } + } + + .send__back__button__disabled { + color: var(--warning); + opacity: 0.6; + &:hover { + text-decoration: underline; + } + } + } } .price { diff --git a/src/pages/wallet/AssetChain.tsx b/src/pages/wallet/AssetChain.tsx index ee6051e33..9c01876b5 100644 --- a/src/pages/wallet/AssetChain.tsx +++ b/src/pages/wallet/AssetChain.tsx @@ -1,13 +1,13 @@ -import { useNetworks } from "app/InitNetworks" import { WithFetching } from "components/feedback" import { Read, TokenIcon } from "components/token" import { useExchangeRates } from "data/queries/coingecko" import { useCurrency } from "data/settings/Currency" -import { useNetworkName } from "data/wallet" +import { useNetwork } from "data/wallet" import { useTranslation } from "react-i18next" import styles from "./AssetChain.module.scss" -import CopyTokenAddress from "./CopyTokenAddress" -import { useDevMode } from "utils/localStorage" +import IbcSendBack from "./IbcSendBack" +import { InternalButton } from "components/general" +import { Tooltip } from "components/display" export interface Props { chain: string @@ -15,25 +15,69 @@ export interface Props { symbol: string decimals: number token: string + path?: string[] + ibcDenom?: string } const AssetChain = (props: Props) => { - const { chain, symbol, balance, decimals, token } = props + const { chain, symbol, balance, decimals, token, path, ibcDenom } = props const currency = useCurrency() const { data: prices, ...pricesState } = useExchangeRates() const { t } = useTranslation() - const { devMode } = useDevMode() - const networkName = useNetworkName() - const { networks } = useNetworks() - const { icon, name } = networks[networkName][chain] + const networks = useNetwork() + + const { icon, name } = networks[chain] + + // send back is not available if one of the chains the asset went through is not supprted by Station + const isSendBackDisabled = !!path?.find((chain) => !networks[chain]) + return (

- {name} - {devMode && } +

+ {name} + {ibcDenom && + path && + (isSendBackDisabled ? ( + +

+ {t( + "This asset originates from an unsupported chain and cannot be sent back." + )} +

+

+ } + > +

+ {t("Send back")} +

+ + ) : ( + + {(open) => ( + !isSendBackDisabled && open()} + className={styles.send__back__button} + disabled={isSendBackDisabled} + > + {t("Send back")} + + )} + + ))} +

+ {path &&

{path.map((c) => networks[c]?.name ?? c).join(" → ")}

}

{currency.symbol}{" "} diff --git a/src/pages/wallet/AssetList.tsx b/src/pages/wallet/AssetList.tsx index 63b53833e..355a25a00 100644 --- a/src/pages/wallet/AssetList.tsx +++ b/src/pages/wallet/AssetList.tsx @@ -10,41 +10,84 @@ import Asset from "./Asset" import styles from "./AssetList.module.scss" import { useTokenFilters } from "utils/localStorage" import { toInput } from "txs/utils" +import { useIBCBaseDenoms } from "data/queries/ibc" +import { useNetwork } from "data/wallet" import { isNativeToken } from "utils/chain" const AssetList = () => { const { t } = useTranslation() const isWalletEmpty = useIsWalletEmpty() const { hideNoWhitelist, hideLowBal } = useTokenFilters() + const networks = useNetwork() const coins = useBankBalance() const { data: prices } = useExchangeRates() const readNativeDenom = useNativeDenoms() + const unknownIBCDenomsData = useIBCBaseDenoms( + coins + .map(({ denom, chain }) => ({ denom, chainID: chain })) + .filter(({ denom }) => { + const data = readNativeDenom(denom) + return denom.startsWith("ibc/") && data.symbol.endsWith("...") + }) + ) + const unknownIBCDenoms = unknownIBCDenomsData.reduce( + (acc, { data }) => + data + ? { + ...acc, + [data.ibcDenom]: { + baseDenom: data.baseDenom, + chainID: data.chainIDs[0], + chainIDs: data.chainIDs, + }, + } + : acc, + {} as Record< + string, + { baseDenom: string; chainID: string; chainIDs: string[] } + > + ) + const list = useMemo( () => [ ...Object.values( coins.reduce((acc, { denom, amount, chain }) => { - const data = readNativeDenom(denom) - if (acc[data.token]) { - acc[data.token].balance = `${ - parseInt(acc[data.token].balance) + parseInt(amount) + const data = readNativeDenom( + unknownIBCDenoms[denom]?.baseDenom ?? denom, + unknownIBCDenoms[denom]?.chainID ?? chain + ) + + const key = [ + // @ts-expect-error + unknownIBCDenoms[denom]?.chainID ?? data.chainID ?? chain, + data.token, + ].join(":") + + if (acc[key]) { + acc[key].balance = `${ + parseInt(acc[key].balance) + parseInt(amount) }` - acc[data.token].chains.push(chain) + acc[key].chains.push(chain) return acc } else { - const isWhitelisted = !denom.endsWith("...") return { ...acc, - [data.token]: { - denom, + [key]: { + denom: data.token, balance: amount, icon: data.icon, symbol: data.symbol, - price: isWhitelisted ? prices?.[data.token]?.price ?? 0 : 0, - change: isWhitelisted ? prices?.[data.token]?.change ?? 0 : 0, + price: prices?.[data.token]?.price ?? 0, + change: prices?.[data.token]?.change ?? 0, chains: [chain], + id: key, + whitelisted: !( + data.symbol.endsWith("...") || + unknownIBCDenoms[denom]?.chainIDs.find((c) => !networks[c]) + ), }, } } @@ -52,7 +95,7 @@ const AssetList = () => { ), ] .filter( - (a) => (hideNoWhitelist ? !a.symbol.endsWith("...") : true) // TODO: update and implement whitelist check + (a) => (hideNoWhitelist ? a.whitelisted : true) // TODO: update and implement whitelist check ) .filter((a) => { if (!(hideLowBal && a.price === 0) || isNativeToken(a.denom)) @@ -63,7 +106,15 @@ const AssetList = () => { (a, b) => b.price * parseInt(b.balance) - a.price * parseInt(a.balance) ), - [coins, readNativeDenom, prices, hideNoWhitelist, hideLowBal] + [ + coins, + readNativeDenom, + prices, + hideNoWhitelist, + hideLowBal, + unknownIBCDenoms, + networks, + ] ) const render = () => { @@ -75,12 +126,16 @@ const AssetList = () => { {t("Coins required to post transactions")} )}
- {list.map(({ denom, ...item }) => ( + {list.map(({ denom, chainID, id, ...item }, i) => ( ))}
diff --git a/src/pages/wallet/AssetPage.tsx b/src/pages/wallet/AssetPage.tsx index f6d90e2c6..cbca3c45a 100644 --- a/src/pages/wallet/AssetPage.tsx +++ b/src/pages/wallet/AssetPage.tsx @@ -11,6 +11,7 @@ import { useTranslation } from "react-i18next" import { capitalize } from "@mui/material" import Vesting from "./Vesting" import { isTerraChain } from "utils/chain" +import { useIBCBaseDenoms } from "data/queries/ibc" const AssetPage = () => { const currency = useCurrency() @@ -19,18 +20,50 @@ const AssetPage = () => { const readNativeDenom = useNativeDenoms() const { t } = useTranslation() const { setRoute, route } = useWalletRoute() - const denom = route.path === Path.coin ? route.denom : "uluna" - const { token, symbol, icon, decimals } = readNativeDenom(denom) + const routeDenom = route.path === Path.coin ? route.denom ?? "uluna" : "uluna" + const [chain, denom] = routeDenom.includes(":") + ? routeDenom.split(":") + : [undefined, routeDenom] + const { token, symbol, icon, decimals } = readNativeDenom(denom, chain) const filteredBalances = balances.filter( (b) => readNativeDenom(b.denom).token === token ) - const totalBalance = filteredBalances.reduce( - (acc, b) => acc + parseInt(b.amount), - 0 - ) + const price = symbol?.endsWith("...") ? 0 : prices?.[token]?.price ?? 0 + const unknownIBCDenomsData = useIBCBaseDenoms( + balances + .map(({ denom, chain }) => ({ denom, chainID: chain })) + .filter(({ denom }) => { + const data = readNativeDenom(denom) + return denom.startsWith("ibc/") && data.symbol.endsWith("...") + }) + ) + + const unknownIBCDenoms = unknownIBCDenomsData.reduce( + (acc, { data }) => + data + ? { + ...acc, + [data.ibcDenom]: { + baseDenom: data.baseDenom, + chains: data.chainIDs, + }, + } + : acc, + {} as Record + ) + + const filteredUnsupportedBalances = balances.filter( + (b) => unknownIBCDenoms[b.denom]?.baseDenom === token + ) + + const totalBalance = [ + ...filteredBalances, + ...filteredUnsupportedBalances, + ].reduce((acc, b) => acc + parseInt(b.amount), 0) + return ( <>
@@ -50,24 +83,51 @@ const AssetPage = () => {

-

{t("Chains")}

-
- {filteredBalances - .sort((a, b) => parseInt(b.amount) - parseInt(a.amount)) - .map((b, i) => ( -
- - {token === "uluna" && isTerraChain(b.chain) && } -
- ))} -
+ {filteredBalances.length > 0 && ( +
+

{t("Chains")}

+
+ {filteredBalances + .sort((a, b) => parseInt(b.amount) - parseInt(a.amount)) + .map((b, i) => ( +
+ + {token === "uluna" && isTerraChain(b.chain) && } +
+ ))} +
+
+ )} + {filteredUnsupportedBalances.length > 0 && ( +
+

{t("Unsupported Chains")}

+
+ {filteredUnsupportedBalances + .sort((a, b) => parseInt(b.amount) - parseInt(a.amount)) + .map((b, i) => ( +
+ +
+ ))} +
+
+ )}
+
diff --git a/src/pages/wallet/Coins.tsx b/src/pages/wallet/Coins.tsx index 32134ad3d..7b6b158e1 100644 --- a/src/pages/wallet/Coins.tsx +++ b/src/pages/wallet/Coins.tsx @@ -32,12 +32,17 @@ const Coins = () => { */}
- {coins.map(({ denom, ...item }) => ( + {coins.map(({ denom, chain, ...item }) => ( ))} diff --git a/src/pages/wallet/IbcSendBack.tsx b/src/pages/wallet/IbcSendBack.tsx new file mode 100644 index 000000000..9bd2c7a6c --- /dev/null +++ b/src/pages/wallet/IbcSendBack.tsx @@ -0,0 +1,25 @@ +import { RenderButton } from "types/components" +import { ModalButton } from "components/feedback" +import IbcSendBackTx from "./IbcSendBackTx" + +interface Props { + children: RenderButton + title: string + token: string + chainID: string +} + +const IbcSendBack = ({ + children: renderButton, + title, + token, + chainID, +}: Props) => { + return ( + + + + ) +} + +export default IbcSendBack diff --git a/src/pages/wallet/IbcSendBackTx.module.scss b/src/pages/wallet/IbcSendBackTx.module.scss new file mode 100644 index 000000000..d50cf37cf --- /dev/null +++ b/src/pages/wallet/IbcSendBackTx.module.scss @@ -0,0 +1,63 @@ +@import "mixins"; + +.steps__container { + @include flex(space-between); + margin-bottom: 1.2rem; + + .chain__pill, + .chain__pill__active { + border-radius: 50%; + padding: 8px; + background: hsl( + var(--button-default-bg-h), + var(--button-default-bg-s), + var(--button-default-bg-l) + ); + img { + width: 26px; + } + } + + .chain__pill__active { + background: hsl( + var(--button-primary-bg-h), + var(--button-primary-bg-s), + var(--button-primary-bg-l) + ); + } + + .chain__path, + .chain__path__active { + height: 8px; + border-radius: 4px; + width: 100%; + background: hsl( + var(--button-default-bg-h), + var(--button-default-bg-s), + var(--button-default-bg-l) + ); + color: hsl( + var(--button-primary-bg-h), + var(--button-primary-bg-s), + var(--button-primary-bg-l) + ); + margin: 0 8px; + } + + .chain__path__active { + background: hsl( + var(--button-primary-bg-h), + var(--button-primary-bg-s), + var(--button-primary-bg-l) + ); + } +} + +/* color */ +.success { + color: var(--success); +} + +.danger { + color: var(--danger); +} diff --git a/src/pages/wallet/IbcSendBackTx.tsx b/src/pages/wallet/IbcSendBackTx.tsx new file mode 100644 index 000000000..545da3f59 --- /dev/null +++ b/src/pages/wallet/IbcSendBackTx.tsx @@ -0,0 +1,347 @@ +import { AccAddress, Coin, MsgTransfer } from "@terra-money/feather.js" +import { toAmount } from "@terra-money/terra-utils" +import { useInterchainAddresses } from "auth/hooks/useAddress" +import { Form, FormItem, Input } from "components/form" +import { useBankBalance } from "data/queries/bank" +import { calculateIBCDenom, useIBCBaseDenom } from "data/queries/ibc" +import { queryKey } from "data/query" +import { useNativeDenoms } from "data/token" +import { useCallback, useEffect, useMemo, useState } from "react" +import { useForm } from "react-hook-form" +import { useTranslation } from "react-i18next" +import Tx from "txs/Tx" +import { CoinInput, getPlaceholder, toInput } from "txs/utils" +import validate from "txs/validate" +import styles from "./IbcSendBackTx.module.scss" +import { useNetwork } from "data/wallet" +import { FlexColumn } from "components/layout" +import { useThemeAnimation } from "data/settings/Theme" +import DoneAllIcon from "@mui/icons-material/DoneAll" +import { LinearProgress } from "@mui/material" +import { useInterchainLCDClient } from "data/queries/lcdClient" +import { useQueryClient } from "react-query" +import ReportIcon from "@mui/icons-material/Report" + +interface Props { + token: string + chainID: string +} + +interface TxValues { + input?: number +} + +function Steps({ + step, + chains, + isLoading, +}: { + step: number + chains: string[] + isLoading: boolean +}) { + const networks = useNetwork() + + return ( +
+ {chains.map((chain, i) => ( + <> + {!!i && ( +
+ {i === step && isLoading && ( + + )} +
+ )} +
+ {chain} +
+ + ))} +
+ ) +} + +function IbcSendBackTx({ token, chainID }: Props) { + const balances = useBankBalance() + const lcd = useInterchainLCDClient() + const readNativeDenom = useNativeDenoms() + const animation = useThemeAnimation() + const { t } = useTranslation() + const addresses = useInterchainAddresses() + const { data: ibcDetails, ...state } = useIBCBaseDenom(token, chainID, true) + const form = useForm({ mode: "onChange" }) + const { register, trigger, watch, setValue, handleSubmit, formState } = form + const { errors } = formState + const { input } = watch() + const queryClient = useQueryClient() + + const [step, setStep] = useState(0) + const [isLoading, setIsLoading] = useState(false) + const [error, setError] = useState(undefined) + const [waitUntil, setWaitUntil] = useState< + undefined | { chainID: string; denom: string; balance: number } + >(undefined) + + async function getBalance(denom: string, chainID: string) { + if (!addresses) return 0 + + if (AccAddress.validate(denom)) { + const { balance } = await lcd.wasm.contractQuery<{ balance: Amount }>( + denom, + { balance: { address: addresses[chainID] } } + ) + + return Number(balance ?? 0) + } else { + const [balances] = await lcd.bank.balance(addresses[chainID]) + + const tokenBalance = + balances.toData().find(({ denom: d }) => denom === d)?.amount ?? "0" + + return Number(tokenBalance) + } + } + + useEffect(() => { + // around 3 minutes with a 10 seconds interval + let maxIterations = 18 + + if (waitUntil) { + ;(async () => { + while (maxIterations--) { + console.log(waitUntil) + const tokenBalance = await getBalance( + waitUntil.denom, + waitUntil.chainID + ) + console.log(tokenBalance) + + if (Number(tokenBalance) > waitUntil.balance) { + setWaitUntil(undefined) + setIsLoading(false) + // refetch all balances for the next form + queryClient.invalidateQueries(queryKey.bank.balances) + return + } + + await new Promise((resolve) => setTimeout(resolve, 10_000)) + } + + // if this get's executed, it means that the transaction failed + setWaitUntil(undefined) + setError("Transaction failed.") + })() + } + + return () => { + maxIterations = 0 + } + }, [waitUntil]) // eslint-disable-line react-hooks/exhaustive-deps + + const IBCdenom = useMemo( + () => + ibcDetails && + calculateIBCDenom( + ibcDetails.baseDenom, + ibcDetails.channels + .slice(step) + .reduce( + (acc, cur) => + acc + ? [cur.port, cur.channel, acc].join("/") + : [cur.port, cur.channel].join("/"), + "" + ) + ), + [step, ibcDetails] + ) + + const chains = useMemo( + () => ibcDetails?.chainIDs.slice().reverse() ?? [], + [ibcDetails] + ) + const decimals = readNativeDenom(ibcDetails?.baseDenom ?? "").decimals + + const createTx = useCallback( + ({ input }: TxValues) => { + if (!ibcDetails || !addresses || !IBCdenom || input === undefined) return + + const msgs = [ + new MsgTransfer( + ibcDetails.channels[step].port, + ibcDetails.channels[step].channel, + new Coin(IBCdenom, input * 10 ** decimals), + addresses[chains[step]], + addresses[chains[step + 1]], + undefined, + (Date.now() + 120 * 1000) * 1e6 + ), + ] + + return { msgs, chainID: chains[step] } + }, + [ibcDetails, addresses, step, IBCdenom, chains, decimals] + ) + + const onChangeMax = useCallback( + async (input: number) => { + setValue("input", input) + await trigger("input") + }, + [setValue, trigger] + ) + + const coins = [{ input, denom: IBCdenom }] as CoinInput[] + const amount = toAmount(input, { decimals }) + const balance = + balances.find( + ({ denom, chain }) => denom === IBCdenom && chain === chains[step] + )?.amount ?? "0" + + const tx = { + token: IBCdenom ?? "", + baseDenom: ibcDetails?.baseDenom, + decimals, + amount, + coins, + chain: chains[step], + disabled: false, + balance, + estimationTxValues: { input: 1 }, + createTx, + onChangeMax, + onPost: () => { + const nextDenom = calculateIBCDenom( + ibcDetails?.baseDenom ?? "", + (ibcDetails?.channels ?? []) + .slice(step + 1) + .reduce( + (acc, cur) => + acc + ? [cur.port, cur.channel, acc].join("/") + : [cur.port, cur.channel].join("/"), + "" + ) + ) + + // wait until balance on the other chain is increased + getBalance(nextDenom, chains[step + 1]).then((balance) => + setWaitUntil({ chainID: chains[step + 1], denom: nextDenom, balance }) + ) + + setStep((step) => step + 1) + setIsLoading(true) + }, + hideLoader: true, + taxRequired: true, + queryKeys: [queryKey.bank.balances, queryKey.bank.balance], + gasAdjustment: 2, + } + + function renderForm() { + return ( + // @ts-expect-error + + {({ max, fee, submit }) => ( +
+ + + + + {fee.render()} + + {submit.button} +
+ )} +
+ ) + } + + return ( +
+ + {(() => { + if (error) { + return ( + + +

{error}

+
+ ) + } + + if (state.isLoading || isLoading) { + return ( + + {t("Loading...")} + {state.isLoading &&

{t("Loading...")}

} + {isLoading && ( + <> +

{t("Waiting fo on-chain confirmation...")}

+

{t("This may take a few minutes")}

+ + )} +
+ ) + } + + if (step === chains.length - 1) { + return ( + + +

{t("Success!")}

+
+ ) + } + + return renderForm() + })()} +
+ ) +} + +export default IbcSendBackTx diff --git a/src/pages/wallet/SendPage.tsx b/src/pages/wallet/SendPage.tsx index 3cbcae752..7546967ae 100644 --- a/src/pages/wallet/SendPage.tsx +++ b/src/pages/wallet/SendPage.tsx @@ -345,11 +345,13 @@ const SendPage = () => { })} autoFocus > - {availableAssets.map(({ denom, symbol }, i) => ( - - ))} + {availableAssets + .filter(({ symbol }) => !symbol.endsWith("...")) + .map(({ denom, symbol }, i) => ( + + ))} {availableChains && ( diff --git a/src/txs/Tx.tsx b/src/txs/Tx.tsx index e0bdfb482..59ffe850d 100644 --- a/src/txs/Tx.tsx +++ b/src/txs/Tx.tsx @@ -48,6 +48,7 @@ import { useNativeDenoms } from "data/token" interface Props { /* Only when the token is paid out of the balance held */ token?: Token + baseDenom?: string decimals?: number amount?: Amount coins?: CoinInput[] @@ -68,6 +69,7 @@ interface Props { /* on tx success */ onPost?: () => void + hideLoader?: boolean onSuccess?: () => void redirectAfterTx?: { label: string; path: string } queryKeys?: QueryKey[] @@ -81,7 +83,8 @@ interface RenderProps { } function Tx(props: Props) { - const { token, decimals, amount, balance, chain } = props + const { token, decimals, amount, balance, chain, baseDenom, hideLoader } = + props const { estimationTxValues, createTx, gasAdjustment: txGasAdjustment } = props const { children, onChangeMax } = props const { onPost, redirectAfterTx, queryKeys, onSuccess } = props @@ -124,9 +127,10 @@ function Tx(props: Props) { const key = { address: addresses?.[chain], - network: networks, + //network: networks, gasAdjustment: gasAdjustment * (txGasAdjustment ?? 1), - msgs: simulationTx?.msgs.map((msg) => msg.toData(isClassic)), + estimationTxValues, + //msgs: simulationTx?.msgs.map((msg) => msg.toData(isClassic)), } const { data: estimatedGas, ...estimatedGasState } = useQuery( [queryKey.tx.create, key, isWalletEmpty], @@ -271,10 +275,10 @@ function Tx(props: Props) { navigate(toPostMultisigTx(unsignedTx)) } else if (wallet) { const { txhash } = await auth.post({ ...tx, fee }, password) - setLatestTx({ txhash, ...latestTxBase }) + !hideLoader && setLatestTx({ txhash, ...latestTxBase }) } else { const { result } = await post({ ...tx, fee }) - setLatestTx({ txhash: result.txhash, ...latestTxBase }) + !hideLoader && setLatestTx({ txhash: result.txhash, ...latestTxBase }) } onPost?.() @@ -325,7 +329,11 @@ function Tx(props: Props) { fontSize="inherit" className={styles.icon} /> - + ) @@ -355,26 +363,45 @@ function Tx(props: Props) { > {availableGasDenoms.map((denom) => ( ))} )} -
{gasFee.amount && }
+
+ {gasFee.amount && ( + + )} +
{balanceAfterTx && ( <>
{t("Balance")}
- +
{t("Balance after tx")}
From ab28ab11894ef6fe0dd64983618ea55d138a062b Mon Sep 17 00:00:00 2001 From: Alessandro Candeago <54709706+alecande11@users.noreply.github.com> Date: Tue, 30 May 2023 18:04:58 +0200 Subject: [PATCH 21/25] ST-426: show all history (#392) --- src/components/layout/ChainFilter.tsx | 6 +- src/pages/history/History.tsx | 4 +- src/pages/history/HistoryList.tsx | 98 ++++++++++++++++----------- 3 files changed, 61 insertions(+), 47 deletions(-) diff --git a/src/components/layout/ChainFilter.tsx b/src/components/layout/ChainFilter.tsx index 39264b7e7..78024c4e5 100644 --- a/src/components/layout/ChainFilter.tsx +++ b/src/components/layout/ChainFilter.tsx @@ -56,11 +56,9 @@ const ChainFilter = ({ ) const initNetwork = - networks.find((n) => n.chainID === savedChain) ?? networks[0] + network[savedChain ?? ""]?.chainID ?? (all ? undefined : chainID) - const [selectedChain, setChain] = useState( - all ? undefined : initNetwork?.chainID - ) + const [selectedChain, setChain] = useState(initNetwork) let fixedChain: string | undefined if (terraOnly) { diff --git a/src/pages/history/History.tsx b/src/pages/history/History.tsx index be776095d..0d5e293e0 100644 --- a/src/pages/history/History.tsx +++ b/src/pages/history/History.tsx @@ -7,8 +7,8 @@ const History = () => { return ( - - {(chain) => } + + {(chain) => } ) diff --git a/src/pages/history/HistoryList.tsx b/src/pages/history/HistoryList.tsx index 7b1fea2a2..74f2923ca 100644 --- a/src/pages/history/HistoryList.tsx +++ b/src/pages/history/HistoryList.tsx @@ -1,7 +1,7 @@ import { Fragment } from "react" -import { useQuery } from "react-query" +import { useQueries } from "react-query" import axios from "axios" -import { queryKey } from "data/query" +import { combineState, queryKey } from "data/query" import { useNetwork } from "data/wallet" import { Card, Col, Page } from "components/layout" import { Empty } from "components/feedback" @@ -9,12 +9,11 @@ import HistoryItem from "./HistoryItem" import { useInterchainAddresses } from "auth/hooks/useAddress" interface Props { - chainID: string + chainID?: string } const HistoryList = ({ chainID }: Props) => { const addresses = useInterchainAddresses() - const address = addresses?.[chainID] const networks = useNetwork() const LIMIT = 100 @@ -27,49 +26,66 @@ const HistoryList = ({ chainID }: Props) => { "transfer.sender", ] - /* query */ - const { data: history, ...state } = useQuery( - [queryKey.History, networks, address, chainID], - async ({ pageParam = 0 }) => { - const result: any[] = [] - const txhases: string[] = [] + const historyData = useQueries( + Object.keys(addresses ?? {}) + .filter((chain) => !chainID || chain === chainID) + .map((chain) => { + const address = chain && addresses?.[chain] - if (!networks || !networks[chainID] || !networks[chainID].lcd) { - return result - } + return { + queryKey: [queryKey.History, networks?.[chain]?.lcd, address], + queryFn: async () => { + const result: any[] = [] + const txhases: string[] = [] - const requests = await Promise.all( - EVENTS.map((event) => { - return axios.get(`/cosmos/tx/v1beta1/txs`, { - baseURL: networks[chainID].lcd, - params: { - events: `${event}='${address}'`, - //order_by: "ORDER_BY_DESC", - "pagination.offset": pageParam || undefined, - "pagination.reverse": true, - "pagination.limit": LIMIT, - }, - }) - }) - ) + if (!networks?.[chain]?.lcd) { + return result + } - for (const { data } of requests) { - data.tx_responses.forEach((tx) => { - if (!txhases.includes(tx.txhash)) { - result.push(tx) - txhases.push(tx.txhash) - } - }) - } + const requests = await Promise.all( + EVENTS.map((event) => { + return axios.get(`/cosmos/tx/v1beta1/txs`, { + baseURL: networks[chain].lcd, + params: { + events: `${event}='${address}'`, + //order_by: "ORDER_BY_DESC", + "pagination.offset": 0 || undefined, + "pagination.reverse": true, + "pagination.limit": LIMIT, + }, + }) + }) + ) - return result - .sort((a, b) => Number(b.height) - Number(a.height)) - .slice(0, LIMIT) - } + for (const { data } of requests) { + data.tx_responses.forEach((tx) => { + if (!txhases.includes(tx.txhash)) { + result.push(tx) + txhases.push(tx.txhash) + } + }) + } + + return result + .sort((a, b) => Number(b.height) - Number(a.height)) + .slice(0, LIMIT) + .map((tx) => ({ ...tx, chain })) + }, + } + }) ) + const state = combineState(...historyData) + const history = historyData + .reduce((acc, { data }) => (data ? [...acc, ...data] : acc), [] as any[]) + .sort( + (a, b) => + new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime() + ) + .slice(0, LIMIT) + const render = () => { - if (address && !history) return null + if (addresses && !history) return null return !history?.length ? ( @@ -79,7 +95,7 @@ const HistoryList = ({ chainID }: Props) => { {history.map((item) => ( - + ))} From cb843f078b18434eca2b69faf85d20c596ad2a3a Mon Sep 17 00:00:00 2001 From: Manuel Alessandro Collazo Date: Wed, 31 May 2023 14:13:17 +0100 Subject: [PATCH 22/25] style: asset chain spacing (#402) --- src/pages/wallet/AssetChain.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/wallet/AssetChain.tsx b/src/pages/wallet/AssetChain.tsx index 9c01876b5..7750188c6 100644 --- a/src/pages/wallet/AssetChain.tsx +++ b/src/pages/wallet/AssetChain.tsx @@ -108,7 +108,7 @@ const AssetChain = (props: Props) => { )} )} - + {" "} {symbol}

From 1fca878ec604ea84060175e5ec6d7632904ceb4e Mon Sep 17 00:00:00 2001 From: Alessandro Candeago <54709706+alecande11@users.noreply.github.com> Date: Wed, 31 May 2023 14:52:46 +0200 Subject: [PATCH 23/25] Fix native tokens whitelist --- src/app/InitBankBalance.tsx | 27 +++++++++++++++---------- src/data/settings/CustomTokens.ts | 10 ++++++--- src/pages/custom/ManageCustomTokens.tsx | 18 ++++++++++++++--- src/types/settings.d.ts | 1 + src/utils/localStorage/keys.ts | 12 +++++++---- 5 files changed, 47 insertions(+), 21 deletions(-) diff --git a/src/app/InitBankBalance.tsx b/src/app/InitBankBalance.tsx index d8082cb0a..55a3bf1f5 100644 --- a/src/app/InitBankBalance.tsx +++ b/src/app/InitBankBalance.tsx @@ -5,19 +5,19 @@ import { useInitialTokenBalance, } from "data/queries/bank" import { BankBalanceProvider } from "data/queries/bank" -//import { useNetworkName } from "data/wallet" +import { useNetworkName } from "data/wallet" import { combineState } from "data/query" import { WithFetching } from "components/feedback" -//import { useCustomTokensNative } from "data/settings/CustomTokens" -//import { useWhitelist } from "data/queries/chains" +import { useCustomTokensNative } from "data/settings/CustomTokens" +import { useWhitelist } from "data/queries/chains" const InitBankBalance = ({ children }: PropsWithChildren<{}>) => { const balances = useInitialBankBalance() const tokenBalancesQuery = useInitialTokenBalance() - //const native = useCustomTokensNative() - //const { whitelist } = useWhitelist() + const native = useCustomTokensNative() + const { whitelist } = useWhitelist() - //const networkName = useNetworkName() + const networkName = useNetworkName() const state = combineState(...balances, ...tokenBalancesQuery) const bankBalance = balances.reduce( @@ -29,19 +29,24 @@ const InitBankBalance = ({ children }: PropsWithChildren<{}>) => { [] as CoinBalance[] ) - /*native.list.forEach(({ denom }) => { - if (!bankBalance.find((balance) => balance.denom === denom)) { - const token = whitelist[networkName][denom] + native.list.forEach(({ id }) => { + const [chain, denom] = id.split(":") + if ( + !bankBalance.find( + (balance) => balance.denom === denom && balance.chain === chain + ) + ) { + const token = whitelist[networkName][id] if (!token || !token.chains || token.chains.length === 0) return bankBalance.push({ denom, amount: "0", - chain: token.chains[0], + chain, }) } - })*/ + }) return ( diff --git a/src/data/settings/CustomTokens.ts b/src/data/settings/CustomTokens.ts index b6da4d29a..2b1bc0845 100644 --- a/src/data/settings/CustomTokens.ts +++ b/src/data/settings/CustomTokens.ts @@ -2,7 +2,7 @@ import { atom, useRecoilState } from "recoil" import update from "immutability-helper" import { DefaultCustomTokensItem, SettingKey } from "utils/localStorage" import { getLocalSetting, setLocalSetting } from "utils/localStorage" -import { useNetworkName } from "../wallet" +import { useChainID, useNetworkName } from "../wallet" const customTokensState = atom({ key: "customTokens", @@ -17,13 +17,17 @@ interface Params { const useCustomTokens = ({ type, key }: Params) => { const [customTokens, setCustomTokens] = useRecoilState(customTokensState) const networkName = useNetworkName() + const chainID = useChainID() const list = (customTokens[networkName]?.[type] ?? []) as T[] const getIsAdded = (param: T) => !!list.find((item) => item[key] === param[key]) const updateList = (list: T[]) => { - const prev = { [networkName]: DefaultCustomTokensItem, ...customTokens } + const prev = { + [networkName]: DefaultCustomTokensItem(chainID), + ...customTokens, + } const next = update(prev, { [networkName]: { [type]: { $set: list } } }) setCustomTokens(next) setLocalSetting(SettingKey.CustomTokens, next) @@ -45,7 +49,7 @@ export const useCustomTokensCW20 = () => { } export const useCustomTokensNative = () => { - return useCustomTokens({ type: "native", key: "denom" }) + return useCustomTokens({ type: "native", key: "id" }) } export const useCustomTokensCW721 = () => { diff --git a/src/pages/custom/ManageCustomTokens.tsx b/src/pages/custom/ManageCustomTokens.tsx index 34c0b1710..735dba4aa 100644 --- a/src/pages/custom/ManageCustomTokens.tsx +++ b/src/pages/custom/ManageCustomTokens.tsx @@ -74,15 +74,27 @@ const Component = ({ whitelist, keyword }: Props) => { list: [...cw20.list, ...native.list], getIsAdded: (item: CustomTokenCW20 | NativeTokenItem) => { if (isCW20(item)) return cw20.getIsAdded(item) - return native.getIsAdded({ denom: item.token }) + const nativeItem = item as NativeTokenItem + return native.getIsAdded({ + id: [nativeItem.chains[0], nativeItem.token].join(":"), + denom: nativeItem.token, + }) }, add: (item: CustomTokenCW20 | NativeTokenItem) => { if (isCW20(item)) return cw20.add(item) - return native.add({ denom: item.token }) + const nativeItem = item as NativeTokenItem + return native.add({ + id: [nativeItem.chains[0], nativeItem.token].join(":"), + denom: nativeItem.token, + }) }, remove: (item: CustomTokenCW20 | NativeTokenItem) => { if (isCW20(item)) return cw20.remove(item) - return native.remove({ denom: item.token }) + const nativeItem = item as NativeTokenItem + return native.remove({ + id: [nativeItem.chains[0], nativeItem.token].join(":"), + denom: nativeItem.token, + }) }, } diff --git a/src/types/settings.d.ts b/src/types/settings.d.ts index 4a4c0170d..36727ce58 100644 --- a/src/types/settings.d.ts +++ b/src/types/settings.d.ts @@ -10,6 +10,7 @@ type CustomTokens = Record interface NativeTokenBasicInfo { denom: CoinDenom + id: string } interface CustomTokensByNetwork { diff --git a/src/utils/localStorage/keys.ts b/src/utils/localStorage/keys.ts index 3da131b69..5023f08e5 100644 --- a/src/utils/localStorage/keys.ts +++ b/src/utils/localStorage/keys.ts @@ -12,7 +12,7 @@ export enum SettingKey { Chain = "Chain", CustomLCD = "CustomLCD", HideLowBalTokens = "HideLowBalTokens", - CustomTokens = "CustomTokens", // Wallet + CustomTokens = "CustomTokensInterchain", // Wallet MinimumValue = "MinimumValue", // Wallet (UST value to show on the list) WithdrawAs = "WithdrawAs", // Rewards (Preferred denom to withdraw rewards) EnabledNetworks = "EnabledNetworks", @@ -27,22 +27,26 @@ export const isSystemDarkMode = export const DefaultTheme = themes[1] -export const DefaultCustomTokensItem = { +export const DefaultCustomTokensItem = (chainID: string) => ({ cw20: [], cw721: [], native: [ { denom: "uluna", + id: `${chainID}:uluna`, }, ], -} +}) + export const DefaultDisplayChains = { mainnet: ["phoenix-1", "osmosis-1"], testnet: ["pisco-1"], classic: ["columbus-5"], } -export const DefaultCustomTokens = { mainnet: DefaultCustomTokensItem } +export const DefaultCustomTokens = { + mainnet: DefaultCustomTokensItem("phoenix-1"), +} export const DefaultSettings = { [SettingKey.Theme]: DefaultTheme, From d41dd5076f746bf59946c06f5babcce3334fe5cc Mon Sep 17 00:00:00 2001 From: Alessandro Candeago <54709706+alecande11@users.noreply.github.com> Date: Wed, 31 May 2023 16:56:27 +0200 Subject: [PATCH 24/25] ST-437: low balance tokens disappearing while prices are being fetched (#399) * fix tokens disappearing with low balance * fix manage tokens list --- src/pages/custom/ManageCustomTokens.tsx | 9 ++--- src/pages/wallet/AssetList.tsx | 45 +++++++++++++++++-------- 2 files changed, 36 insertions(+), 18 deletions(-) diff --git a/src/pages/custom/ManageCustomTokens.tsx b/src/pages/custom/ManageCustomTokens.tsx index 735dba4aa..a728f285d 100644 --- a/src/pages/custom/ManageCustomTokens.tsx +++ b/src/pages/custom/ManageCustomTokens.tsx @@ -116,6 +116,7 @@ const Component = ({ whitelist, keyword }: Props) => { const ManageCustomTokens = () => { const { data: cw20, ...cw20WhitelistState } = useCW20Whitelist() + console.log(cw20) const { whitelist } = useWhitelist() const networkName = useNetworkName() @@ -124,11 +125,11 @@ const ManageCustomTokens = () => { const cw20Whitelist: CW20Whitelist = {} const nativeWhitelist: NativeWhitelist = {} - Object.entries(whitelist[networkName]).forEach(([denom, asset]) => { - if (AccAddress.validate(denom)) { - cw20Whitelist[denom] = asset + Object.entries(whitelist[networkName]).forEach(([id, asset]) => { + if (AccAddress.validate(asset.token)) { + cw20Whitelist[asset.token] = asset } else { - nativeWhitelist[denom] = asset + nativeWhitelist[id] = asset } }) diff --git a/src/pages/wallet/AssetList.tsx b/src/pages/wallet/AssetList.tsx index 355a25a00..359cee6fc 100644 --- a/src/pages/wallet/AssetList.tsx +++ b/src/pages/wallet/AssetList.tsx @@ -10,9 +10,12 @@ import Asset from "./Asset" import styles from "./AssetList.module.scss" import { useTokenFilters } from "utils/localStorage" import { toInput } from "txs/utils" +import { + useCustomTokensCW20, + useCustomTokensNative, +} from "data/settings/CustomTokens" import { useIBCBaseDenoms } from "data/queries/ibc" import { useNetwork } from "data/wallet" -import { isNativeToken } from "utils/chain" const AssetList = () => { const { t } = useTranslation() @@ -23,6 +26,16 @@ const AssetList = () => { const coins = useBankBalance() const { data: prices } = useExchangeRates() const readNativeDenom = useNativeDenoms() + const native = useCustomTokensNative() + const cw20 = useCustomTokensCW20() + const alwaysVisibleDenoms = useMemo( + () => + new Set([ + ...cw20.list.map((a) => a.token), + ...native.list.map((a) => a.denom), + ]), + [cw20.list, native.list] + ) const unknownIBCDenomsData = useIBCBaseDenoms( coins @@ -98,7 +111,9 @@ const AssetList = () => { (a) => (hideNoWhitelist ? a.whitelisted : true) // TODO: update and implement whitelist check ) .filter((a) => { - if (!(hideLowBal && a.price === 0) || isNativeToken(a.denom)) + const { token } = readNativeDenom(a.denom) + + if (!hideLowBal || a.price === 0 || alwaysVisibleDenoms.has(token)) return true return a.price * toInput(a.balance) >= 1 }) @@ -112,6 +127,7 @@ const AssetList = () => { prices, hideNoWhitelist, hideLowBal, + alwaysVisibleDenoms, unknownIBCDenoms, networks, ] @@ -126,18 +142,19 @@ const AssetList = () => { {t("Coins required to post transactions")} )}
- {list.map(({ denom, chainID, id, ...item }, i) => ( - - ))} + {(prices || !hideLowBal) && + list.map(({ denom, chainID, id, ...item }, i) => ( + + ))}
) From 741488c6514636df427cf015c618cc8849a793c7 Mon Sep 17 00:00:00 2001 From: Manuel Alessandro Collazo Date: Wed, 31 May 2023 18:02:56 +0100 Subject: [PATCH 25/25] fix: typo (#405) --- src/pages/wallet/IbcSendBackTx.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/wallet/IbcSendBackTx.tsx b/src/pages/wallet/IbcSendBackTx.tsx index 545da3f59..293553037 100644 --- a/src/pages/wallet/IbcSendBackTx.tsx +++ b/src/pages/wallet/IbcSendBackTx.tsx @@ -318,7 +318,7 @@ function IbcSendBackTx({ token, chainID }: Props) { {state.isLoading &&

{t("Loading...")}

} {isLoading && ( <> -

{t("Waiting fo on-chain confirmation...")}

+

{t("Waiting for on-chain confirmation...")}

{t("This may take a few minutes")}

)}