From 711ab0f37b9ba2df8837f0cd1bb9285d98412be4 Mon Sep 17 00:00:00 2001
From: abefernan <44572727+abefernan@users.noreply.github.com>
Date: Thu, 22 Jun 2023 09:35:14 +0200
Subject: [PATCH 01/13] Add assets to ChainInfo and ChainSelect
---
components/chainSelect/ChainSelect.tsx | 36 +++++++++++++++++++-------
types/index.ts | 1 +
2 files changed, 28 insertions(+), 9 deletions(-)
diff --git a/components/chainSelect/ChainSelect.tsx b/components/chainSelect/ChainSelect.tsx
index 91dce56..138a6b7 100644
--- a/components/chainSelect/ChainSelect.tsx
+++ b/components/chainSelect/ChainSelect.tsx
@@ -62,6 +62,7 @@ const ChainSelect = () => {
const [tempDisplayDenomExponent, setDisplayDenomExponent] = useState(
state.chain.displayDenomExponent,
);
+ const [tempAssets, setAssets] = useState(state.chain.assets);
const [tempGasPrice, setGasPrice] = useState(state.chain.gasPrice);
const [tempChainName, setChainName] = useState(state.chain.chainDisplayName);
const [tempRegistryName, setRegistryName] = useState(state.chain.registryName);
@@ -110,6 +111,7 @@ const ChainSelect = () => {
setDenom(state.chain.denom);
setDisplayDenom(state.chain.displayDenom);
setDisplayDenomExponent(state.chain.displayDenomExponent);
+ setAssets(state.chain.assets);
setGasPrice(state.chain.gasPrice);
setChainName(state.chain.chainDisplayName);
setExplorerLink(state.chain.explorerLink);
@@ -133,25 +135,31 @@ const ChainSelect = () => {
try {
const chainData = await getChainFromRegistry(chainOption.path);
- const assets = await getAssetsFromRegistry(chainOption.path);
- const firstAsset = assets[0];
+ const registryAssets = await getAssetsFromRegistry(chainOption.path);
+ const firstAsset = registryAssets[0];
const nodeAddress = await getNodeFromArray(chainData.apis.rpc);
const explorerLink = getExplorerFromArray(chainData.explorers);
- const denom = firstAsset.base || "";
- const displayDenom = firstAsset.symbol || "";
+ const firstAssetDenom = firstAsset.base;
+ const displayDenom = firstAsset.symbol;
+
+ const assets = registryAssets.flatMap(({ denom_units }) =>
+ denom_units.map(({ denom, exponent }) => ({ denom, exponent })),
+ );
const displayUnit = firstAsset.denom_units.find((u) => u.denom == firstAsset.display);
const displayDenomExponent = displayUnit?.exponent ?? 6;
- const feeToken = chainData.fees.fee_tokens.find((token) => token.denom == denom) ?? { denom };
+ const feeToken = chainData.fees.fee_tokens.find(
+ (token) => token.denom == firstAssetDenom,
+ ) ?? { denom: firstAssetDenom };
const gasPrice =
feeToken.average_gas_price ??
feeToken.low_gas_price ??
feeToken.high_gas_price ??
feeToken.fixed_min_gas_price ??
0.03;
- const formattedGasPrice = firstAsset ? `${gasPrice}${denom}` : "";
+ const formattedGasPrice = firstAsset ? `${gasPrice}${firstAssetDenom}` : "";
// change app state
dispatch({
@@ -163,10 +171,11 @@ const ChainSelect = () => {
chainDisplayName: chainData.pretty_name,
nodeAddress,
explorerLink,
- denom,
+ denom: firstAssetDenom,
displayDenom,
displayDenomExponent,
gasPrice: formattedGasPrice,
+ assets,
},
});
@@ -253,6 +262,7 @@ const ChainSelect = () => {
denom: tempDenom,
displayDenom: tempDisplayDenom,
displayDenomExponent: tempDisplayDenomExponent,
+ assets: tempAssets,
gasPrice: tempGasPrice,
chainId: tempChainId,
chainDisplayName: tempChainName,
@@ -367,14 +377,22 @@ const ChainSelect = () => {
}
label="Denom Exponent"
/>
+ ) =>
+ setAssets(JSON.parse(e.target.value))
+ }
+ label="Assets"
+ />
+
+
) => setGasPrice(e.target.value)}
label="Gas Price"
/>
-
-
Date: Thu, 22 Jun 2023 09:35:34 +0200
Subject: [PATCH 02/13] Serialize array params like assets
---
context/AppContext.tsx | 18 ++++++++++--------
1 file changed, 10 insertions(+), 8 deletions(-)
diff --git a/context/AppContext.tsx b/context/AppContext.tsx
index f58e2ca..689b5ab 100644
--- a/context/AppContext.tsx
+++ b/context/AppContext.tsx
@@ -1,7 +1,6 @@
-import React, { useEffect, createContext, useContext, useReducer } from "react";
-
-import { AppReducer, ChangeChainAction, initialState } from "./AppReducer";
+import { createContext, useContext, useEffect, useReducer } from "react";
import { ChainInfo } from "../types";
+import { AppReducer, ChangeChainAction, initialState } from "./AppReducer";
export interface AppContextType {
chain: ChainInfo;
@@ -23,6 +22,7 @@ function getChainInfoFromUrl(): ChainInfo {
decodeURIComponent(params.get("displayDenomExponent") || ""),
10,
),
+ assets: JSON.parse(decodeURIComponent(params.get("assets") || "{}")),
gasPrice: decodeURIComponent(params.get("gasPrice") || ""),
chainId: decodeURIComponent(params.get("chainId") || ""),
chainDisplayName: decodeURIComponent(params.get("chainDisplayName") || ""),
@@ -37,11 +37,13 @@ function getChainInfoFromUrl(): ChainInfo {
function setChainInfoParams(chainInfo: ChainInfo) {
const params = new URLSearchParams();
- const keys = Object.keys(chainInfo) as Array;
-
- keys.forEach((value: keyof ChainInfo) => {
- params.set(value, encodeURIComponent(chainInfo[value] || ""));
- });
+ for (const [key, value] of Object.entries(chainInfo)) {
+ if (Array.isArray(value)) {
+ params.set(key, encodeURIComponent(JSON.stringify(value)));
+ } else {
+ params.set(key, encodeURIComponent(value ?? ""));
+ }
+ }
window.history.replaceState({}, "", `${location.pathname}?${params}`);
}
From ae66458d39a639f148435f6380bb4ec9ec1d0f2c Mon Sep 17 00:00:00 2001
From: abefernan <44572727+abefernan@users.noreply.github.com>
Date: Thu, 22 Jun 2023 09:36:04 +0200
Subject: [PATCH 03/13] Add select and custom denom in MsgSendForm
---
.../CreateTxForm/MsgForm/MsgSendForm.tsx | 153 ++++++++++++++----
1 file changed, 121 insertions(+), 32 deletions(-)
diff --git a/components/forms/CreateTxForm/MsgForm/MsgSendForm.tsx b/components/forms/CreateTxForm/MsgForm/MsgSendForm.tsx
index dda14ff..875461b 100644
--- a/components/forms/CreateTxForm/MsgForm/MsgSendForm.tsx
+++ b/components/forms/CreateTxForm/MsgForm/MsgSendForm.tsx
@@ -5,10 +5,39 @@ import { useEffect, useState } from "react";
import { MsgGetter } from "..";
import { useAppContext } from "../../../../context/AppContext";
import { checkAddress, exampleAddress } from "../../../../lib/displayHelpers";
+import { ChainInfo } from "../../../../types";
import { MsgCodecs, MsgTypeUrls } from "../../../../types/txMsg";
import Input from "../../../inputs/Input";
+import Select from "../../../inputs/Select";
import StackableContainer from "../../../layout/StackableContainer";
+const getDenomOptions = (assets: ChainInfo["assets"]) => {
+ const customDenomOption = { label: "Custom (enter denom below)", value: "custom" };
+ if (!assets?.length) {
+ return [customDenomOption];
+ }
+
+ const filteredAssets = assets.filter(({ denom: denomToFilter }) => {
+ if (denomToFilter.startsWith("u")) {
+ const foundMacroDenom = assets.find(
+ ({ denom }) => denom.toLowerCase() === denomToFilter.slice(1).toLowerCase(),
+ );
+ if (foundMacroDenom) {
+ return false;
+ }
+ }
+
+ return true;
+ });
+
+ const denomOptions = filteredAssets.map(({ denom }) => ({
+ label: denom.toUpperCase(),
+ value: denom,
+ }));
+
+ return [...denomOptions, customDenomOption];
+};
+
interface MsgSendFormProps {
readonly fromAddress: string;
readonly setMsgGetter: (msgGetter: MsgGetter) => void;
@@ -19,60 +48,85 @@ const MsgSendForm = ({ fromAddress, setMsgGetter, deleteMsg }: MsgSendFormProps)
const { state } = useAppContext();
assert(state.chain.addressPrefix, "addressPrefix missing");
+ const denomOptions = getDenomOptions(state.chain.assets);
+
const [toAddress, setToAddress] = useState("");
+ const [selectedDenom, setSelectedDenom] = useState(denomOptions[0]);
+ const [customDenom, setCustomDenom] = useState("");
const [amount, setAmount] = useState("0");
const [toAddressError, setToAddressError] = useState("");
+ const [customDenomError, setCustomDenomError] = useState("");
const [amountError, setAmountError] = useState("");
useEffect(() => {
- try {
- assert(state.chain.denom, "denom missing");
+ assert(state.chain.denom, "denom missing");
- setToAddressError("");
- setAmountError("");
+ setToAddressError("");
+ setCustomDenomError("");
+ setAmountError("");
- const isMsgValid = (): boolean => {
- assert(state.chain.addressPrefix, "addressPrefix missing");
+ const isMsgValid = (): boolean => {
+ assert(state.chain.addressPrefix, "addressPrefix missing");
- const addressErrorMsg = checkAddress(toAddress, state.chain.addressPrefix);
- if (addressErrorMsg) {
- setToAddressError(
- `Invalid address for network ${state.chain.chainId}: ${addressErrorMsg}`,
- );
- return false;
- }
+ const addressErrorMsg = checkAddress(toAddress, state.chain.addressPrefix);
+ if (addressErrorMsg) {
+ setToAddressError(`Invalid address for network ${state.chain.chainId}: ${addressErrorMsg}`);
+ return false;
+ }
- if (!amount || Number(amount) <= 0) {
- setAmountError("Amount must be greater than 0");
- return false;
- }
+ if (selectedDenom.value === "custom" && !customDenom) {
+ setCustomDenomError("Custom denom must be set because of selection above");
+ return false;
+ }
- return true;
- };
+ if (!amount || Number(amount) <= 0) {
+ setAmountError("Amount must be greater than 0");
+ return false;
+ }
- const amountInAtomics = amount
- ? Decimal.fromUserInput(amount, Number(state.chain.displayDenomExponent)).atomics
- : "0";
+ if (selectedDenom.value === "custom" && !Number.isInteger(amount)) {
+ setAmountError("Amount cannot be decimal for custom denom");
+ return false;
+ }
- const msgValue = MsgCodecs[MsgTypeUrls.Send].fromPartial({
- fromAddress,
- toAddress,
- amount: [{ amount: amountInAtomics, denom: state.chain.denom }],
- });
+ return true;
+ };
- const msg: MsgSendEncodeObject = { typeUrl: MsgTypeUrls.Send, value: msgValue };
+ const denom = (
+ selectedDenom.value === "custom" ? customDenom : selectedDenom.value
+ ).toLowerCase();
+ const exponent =
+ state.chain.assets?.find(({ denom: currentDenom }) => currentDenom.toLowerCase() === denom)
+ ?.exponent ?? 0;
- setMsgGetter({ isMsgValid, msg });
- } catch {}
+ const amountInAtomics = (() => {
+ try {
+ return Decimal.fromUserInput(amount, exponent).atomics;
+ } catch {
+ return "0";
+ }
+ })();
+
+ const msgValue = MsgCodecs[MsgTypeUrls.Send].fromPartial({
+ fromAddress,
+ toAddress,
+ amount: [{ denom, amount: amountInAtomics }],
+ });
+
+ const msg: MsgSendEncodeObject = { typeUrl: MsgTypeUrls.Send, value: msgValue };
+
+ setMsgGetter({ isMsgValid, msg });
}, [
amount,
+ customDenom,
fromAddress,
+ selectedDenom.value,
setMsgGetter,
state.chain.addressPrefix,
+ state.chain.assets,
state.chain.chainId,
state.chain.denom,
- state.chain.displayDenomExponent,
toAddress,
]);
@@ -92,10 +146,36 @@ const MsgSendForm = ({ fromAddress, setMsgGetter, deleteMsg }: MsgSendFormProps)
placeholder={`E.g. ${exampleAddress(0, state.chain.addressPrefix)}`}
/>
+
+
+
+
+ setCustomDenom(target.value)}
+ placeholder="Select Custom denom above"
+ disabled={selectedDenom.value !== "custom"}
+ error={customDenomError}
+ />
+
setAmount(target.value)}
@@ -106,6 +186,15 @@ const MsgSendForm = ({ fromAddress, setMsgGetter, deleteMsg }: MsgSendFormProps)
.form-item {
margin-top: 1.5em;
}
+ .form-item label {
+ font-style: italic;
+ font-size: 12px;
+ }
+ .form-select {
+ display: flex;
+ flex-direction: column;
+ gap: 0.8em;
+ }
button.remove {
background: rgba(255, 255, 255, 0.2);
width: 30px;
From a1cb013dd3643d7506e5b45cb81560e24f776f7f Mon Sep 17 00:00:00 2001
From: abefernan <44572727+abefernan@users.noreply.github.com>
Date: Thu, 22 Jun 2023 09:36:21 +0200
Subject: [PATCH 04/13] Tweak printableCoin to check assets
---
lib/displayHelpers.ts | 31 ++++++++++++++++++-------------
1 file changed, 18 insertions(+), 13 deletions(-)
diff --git a/lib/displayHelpers.ts b/lib/displayHelpers.ts
index 34466b0..e7db53d 100644
--- a/lib/displayHelpers.ts
+++ b/lib/displayHelpers.ts
@@ -37,18 +37,23 @@ const thinSpace = "\u202F";
const printableCoin = (coin: Coin, chainInfo: ChainInfo) => {
if (!coin.amount || !coin.denom) return "";
- // The display denom from configuration
- if (coin.denom === chainInfo.denom) {
- const exponent = Number(chainInfo.displayDenomExponent);
- const value = Decimal.fromAtomics(coin.amount ?? "0", exponent).toString();
- const ticker = chainInfo.displayDenom;
- return value + thinSpace + ticker;
- }
+ // Check if denom is in assets
+ const foundAsset = chainInfo.assets?.find(
+ (asset) => asset.denom.toLowerCase() === coin.denom.toLowerCase(),
+ );
- // Auto-convert leading "u"s
- if (coin.denom.startsWith("u")) {
- const value = Decimal.fromAtomics(coin.amount ?? "0", 6).toString();
- const ticker = coin.denom.slice(1).toUpperCase();
+ // Check if denom starting with "u" has a macrodenom in assets
+ const foundMacrodenom = coin.denom.startsWith("u")
+ ? chainInfo.assets?.find(
+ (asset) => asset.denom.toLowerCase() === coin.denom.slice(1).toLowerCase(),
+ )
+ : undefined;
+
+ const assetToPrint = foundMacrodenom ?? foundAsset;
+
+ if (assetToPrint) {
+ const value = Decimal.fromAtomics(coin.amount ?? "0", assetToPrint.exponent).toString();
+ const ticker = assetToPrint.denom.toUpperCase();
return value + thinSpace + ticker;
}
@@ -57,12 +62,12 @@ const printableCoin = (coin: Coin, chainInfo: ChainInfo) => {
const value = coin.amount;
const hash = coin.denom.slice(4);
const ellidedHash = ellideMiddle(hash, 11);
- const ticker = `ibc/${ellidedHash}`;
+ const ticker = `ibc/${ellidedHash}`.toUpperCase();
return value + thinSpace + ticker;
}
// Fallback to plain coin display
- return coin.amount + thinSpace + coin.denom;
+ return coin.amount + thinSpace + coin.denom.toUpperCase();
};
const printableCoins = (coins: readonly Coin[], chainInfo: ChainInfo) =>
From 06aa02bca9f11307dc8501aa6e8ccc5618ec2317 Mon Sep 17 00:00:00 2001
From: abefernan <44572727+abefernan@users.noreply.github.com>
Date: Thu, 22 Jun 2023 09:39:49 +0200
Subject: [PATCH 05/13] Make custom denom placeholder more helpful
---
components/forms/CreateTxForm/MsgForm/MsgSendForm.tsx | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/components/forms/CreateTxForm/MsgForm/MsgSendForm.tsx b/components/forms/CreateTxForm/MsgForm/MsgSendForm.tsx
index 875461b..1bf9166 100644
--- a/components/forms/CreateTxForm/MsgForm/MsgSendForm.tsx
+++ b/components/forms/CreateTxForm/MsgForm/MsgSendForm.tsx
@@ -167,7 +167,9 @@ const MsgSendForm = ({ fromAddress, setMsgGetter, deleteMsg }: MsgSendFormProps)
name="custom-denom"
value={customDenom}
onChange={({ target }) => setCustomDenom(target.value)}
- placeholder="Select Custom denom above"
+ placeholder={
+ selectedDenom.value === "custom" ? "Enter custom denom" : "Select Custom denom above"
+ }
disabled={selectedDenom.value !== "custom"}
error={customDenomError}
/>
From eca93d23cd6732f85fec175531093da84cf40216 Mon Sep 17 00:00:00 2001
From: abefernan <44572727+abefernan@users.noreply.github.com>
Date: Fri, 23 Jun 2023 23:00:42 +0200
Subject: [PATCH 06/13] Fix registry type
---
components/chainSelect/chainregistry.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/components/chainSelect/chainregistry.ts b/components/chainSelect/chainregistry.ts
index 1714f4c..a8f068d 100644
--- a/components/chainSelect/chainregistry.ts
+++ b/components/chainSelect/chainregistry.ts
@@ -46,7 +46,7 @@ export interface RegistryChainResponse {
export interface RegistryAssetDenomUnit {
denom: string;
exponent: number;
- aliases: string[];
+ aliases?: string[];
}
/**
From 9cab79269edca70465e1c9e3aacb30c59c5e2c62 Mon Sep 17 00:00:00 2001
From: abefernan <44572727+abefernan@users.noreply.github.com>
Date: Fri, 23 Jun 2023 23:01:01 +0200
Subject: [PATCH 07/13] Add assets to ChainInfo
---
components/chainSelect/ChainSelect.tsx | 7 +------
types/index.ts | 3 ++-
2 files changed, 3 insertions(+), 7 deletions(-)
diff --git a/components/chainSelect/ChainSelect.tsx b/components/chainSelect/ChainSelect.tsx
index 138a6b7..d9aa6ba 100644
--- a/components/chainSelect/ChainSelect.tsx
+++ b/components/chainSelect/ChainSelect.tsx
@@ -142,11 +142,6 @@ const ChainSelect = () => {
const explorerLink = getExplorerFromArray(chainData.explorers);
const firstAssetDenom = firstAsset.base;
const displayDenom = firstAsset.symbol;
-
- const assets = registryAssets.flatMap(({ denom_units }) =>
- denom_units.map(({ denom, exponent }) => ({ denom, exponent })),
- );
-
const displayUnit = firstAsset.denom_units.find((u) => u.denom == firstAsset.display);
const displayDenomExponent = displayUnit?.exponent ?? 6;
@@ -175,7 +170,7 @@ const ChainSelect = () => {
displayDenom,
displayDenomExponent,
gasPrice: formattedGasPrice,
- assets,
+ assets: registryAssets,
},
});
diff --git a/types/index.ts b/types/index.ts
index 75b0804..b01fb4d 100644
--- a/types/index.ts
+++ b/types/index.ts
@@ -1,5 +1,6 @@
import { StdFee } from "@cosmjs/amino";
import { EncodeObject } from "@cosmjs/proto-signing";
+import { RegistryAsset } from "../components/chainSelect/chainregistry";
declare global {
interface Window {
@@ -51,7 +52,7 @@ export interface ChainInfo {
denom?: string;
displayDenom?: string;
displayDenomExponent?: number;
- assets?: readonly { readonly denom: string; readonly exponent: number }[];
+ assets?: readonly RegistryAsset[];
gasPrice?: string;
chainId?: string;
chainDisplayName?: string;
From 312bfb5ba25ac63e507b65de7500632fc4ea4eb8 Mon Sep 17 00:00:00 2001
From: abefernan <44572727+abefernan@users.noreply.github.com>
Date: Fri, 23 Jun 2023 23:02:19 +0200
Subject: [PATCH 08/13] Fix printableCoin for microdenoms
---
lib/displayHelpers.ts | 33 +++++++++++++--------------------
1 file changed, 13 insertions(+), 20 deletions(-)
diff --git a/lib/displayHelpers.ts b/lib/displayHelpers.ts
index e7db53d..a635ead 100644
--- a/lib/displayHelpers.ts
+++ b/lib/displayHelpers.ts
@@ -37,26 +37,6 @@ const thinSpace = "\u202F";
const printableCoin = (coin: Coin, chainInfo: ChainInfo) => {
if (!coin.amount || !coin.denom) return "";
- // Check if denom is in assets
- const foundAsset = chainInfo.assets?.find(
- (asset) => asset.denom.toLowerCase() === coin.denom.toLowerCase(),
- );
-
- // Check if denom starting with "u" has a macrodenom in assets
- const foundMacrodenom = coin.denom.startsWith("u")
- ? chainInfo.assets?.find(
- (asset) => asset.denom.toLowerCase() === coin.denom.slice(1).toLowerCase(),
- )
- : undefined;
-
- const assetToPrint = foundMacrodenom ?? foundAsset;
-
- if (assetToPrint) {
- const value = Decimal.fromAtomics(coin.amount ?? "0", assetToPrint.exponent).toString();
- const ticker = assetToPrint.denom.toUpperCase();
- return value + thinSpace + ticker;
- }
-
// Ellide IBC tokens
if (coin.denom.startsWith("ibc/")) {
const value = coin.amount;
@@ -66,6 +46,19 @@ const printableCoin = (coin: Coin, chainInfo: ChainInfo) => {
return value + thinSpace + ticker;
}
+ const foundAsset = chainInfo.assets?.find(
+ (asset) => coin.denom === asset.symbol || coin.denom === asset.base,
+ );
+ const foundExponent = foundAsset?.denom_units.find(
+ (unit) => unit.denom === foundAsset.symbol.toLowerCase(),
+ )?.exponent;
+
+ if (foundExponent) {
+ const value = Decimal.fromAtomics(coin.amount, foundExponent).toString();
+ const ticker = foundAsset.symbol;
+ return value + thinSpace + ticker;
+ }
+
// Fallback to plain coin display
return coin.amount + thinSpace + coin.denom.toUpperCase();
};
From 55604efe0e5fce9554e9a95dac8cb916faac10e4 Mon Sep 17 00:00:00 2001
From: abefernan <44572727+abefernan@users.noreply.github.com>
Date: Fri, 23 Jun 2023 23:02:35 +0200
Subject: [PATCH 09/13] Make printableCoin tests pass
---
lib/displayHelpers.spec.ts | 32 +++++++++++++++++++++++++++-----
1 file changed, 27 insertions(+), 5 deletions(-)
diff --git a/lib/displayHelpers.spec.ts b/lib/displayHelpers.spec.ts
index 385c1b0..25e00e9 100644
--- a/lib/displayHelpers.spec.ts
+++ b/lib/displayHelpers.spec.ts
@@ -12,13 +12,35 @@ const testChainInfo: ChainInfo = {
denom: "ujunox",
displayDenom: "JUNOX",
displayDenomExponent: 6,
+ assets: [{
+ description: "The native token of JUNO Chain",
+ denom_units: [
+ {
+ denom: "ujunox",
+ exponent: 0
+ },
+ {
+ denom: "junox",
+ exponent: 6
+ }
+ ],
+ base: "ujunox",
+ name: "Juno Testnet",
+ display: "junox",
+ symbol: "JUNOX",
+ logo_URIs: {
+ png: "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/junotestnet/images/juno.png",
+ svg: "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/junotestnet/images/juno.svg"
+ },
+ coingecko_id: "juno-network"
+ }],
gasPrice: "0.04ujunox",
};
const emptyCoin: Coin = { amount: "", denom: "" };
const coinInChain: Coin = { amount: "1000", denom: "ujunox" };
const coinNotInChainLeading: Coin = { amount: "20000", denom: "utest" };
-const coinNotInChainNotLeading: Coin = { amount: "300000", denom: "test" };
+const coinNotInChainNotLeading: Coin = { amount: "300000", denom: "TEST" };
const ibcCoin: Coin = {
amount: "4000000",
denom: "ibc/c4cff46fd6de35ca4cf4ce031e643c8fdc9ba4b99ae598e9b0ed98fe3a2319f9",
@@ -34,17 +56,17 @@ describe("printableCoin", () => {
});
it("works with coin not in ChainInfo with leading 'u'", () => {
- expect(printableCoin(coinNotInChainLeading, testChainInfo)).toEqual(`0.02${thinSpace}TEST`);
+ expect(printableCoin(coinNotInChainLeading, testChainInfo)).toEqual(`20000${thinSpace}UTEST`);
});
it("works with coin not in ChainInfo without leading 'u'", () => {
expect(printableCoin(coinNotInChainNotLeading, testChainInfo)).toEqual(
- `300000${thinSpace}test`,
+ `300000${thinSpace}TEST`,
);
});
it("works with IBC coin", () => {
- expect(printableCoin(ibcCoin, testChainInfo)).toEqual(`4000000${thinSpace}ibc/c4cff…319f9`);
+ expect(printableCoin(ibcCoin, testChainInfo)).toEqual(`4000000${thinSpace}IBC/C4CFF…319F9`);
});
});
@@ -67,7 +89,7 @@ describe("printableCoins", () => {
];
expect(printableCoins(coins, testChainInfo)).toEqual(
- `0.001${thinSpace}JUNOX, 0.02${thinSpace}TEST, 300000${thinSpace}test, 4000000 ibc/c4cff…319f9`,
+ `0.001${thinSpace}JUNOX, 20000${thinSpace}UTEST, 300000${thinSpace}TEST, 4000000 IBC/C4CFF…319F9`,
);
});
});
From 996b65ae08c4407cd7d89fa1fc1332fc7ca54a19 Mon Sep 17 00:00:00 2001
From: abefernan <44572727+abefernan@users.noreply.github.com>
Date: Fri, 23 Jun 2023 23:03:14 +0200
Subject: [PATCH 10/13] Fix amountInAtomics. Simplify getDenomOptions
---
.../CreateTxForm/MsgForm/MsgSendForm.tsx | 52 ++++++++-----------
1 file changed, 21 insertions(+), 31 deletions(-)
diff --git a/components/forms/CreateTxForm/MsgForm/MsgSendForm.tsx b/components/forms/CreateTxForm/MsgForm/MsgSendForm.tsx
index 1bf9166..6b78740 100644
--- a/components/forms/CreateTxForm/MsgForm/MsgSendForm.tsx
+++ b/components/forms/CreateTxForm/MsgForm/MsgSendForm.tsx
@@ -11,31 +11,14 @@ import Input from "../../../inputs/Input";
import Select from "../../../inputs/Select";
import StackableContainer from "../../../layout/StackableContainer";
+const customDenomOption = { label: "Custom (enter denom below)", value: "custom" } as const;
+
const getDenomOptions = (assets: ChainInfo["assets"]) => {
- const customDenomOption = { label: "Custom (enter denom below)", value: "custom" };
if (!assets?.length) {
return [customDenomOption];
}
- const filteredAssets = assets.filter(({ denom: denomToFilter }) => {
- if (denomToFilter.startsWith("u")) {
- const foundMacroDenom = assets.find(
- ({ denom }) => denom.toLowerCase() === denomToFilter.slice(1).toLowerCase(),
- );
- if (foundMacroDenom) {
- return false;
- }
- }
-
- return true;
- });
-
- const denomOptions = filteredAssets.map(({ denom }) => ({
- label: denom.toUpperCase(),
- value: denom,
- }));
-
- return [...denomOptions, customDenomOption];
+ return [...assets.map((asset) => ({ label: asset.symbol, value: asset })), customDenomOption];
};
interface MsgSendFormProps {
@@ -75,7 +58,7 @@ const MsgSendForm = ({ fromAddress, setMsgGetter, deleteMsg }: MsgSendFormProps)
return false;
}
- if (selectedDenom.value === "custom" && !customDenom) {
+ if (selectedDenom.value === customDenomOption.value && !customDenom) {
setCustomDenomError("Custom denom must be set because of selection above");
return false;
}
@@ -85,7 +68,7 @@ const MsgSendForm = ({ fromAddress, setMsgGetter, deleteMsg }: MsgSendFormProps)
return false;
}
- if (selectedDenom.value === "custom" && !Number.isInteger(amount)) {
+ if (selectedDenom.value === customDenomOption.value && !Number.isInteger(Number(amount))) {
setAmountError("Amount cannot be decimal for custom denom");
return false;
}
@@ -93,15 +76,20 @@ const MsgSendForm = ({ fromAddress, setMsgGetter, deleteMsg }: MsgSendFormProps)
return true;
};
- const denom = (
- selectedDenom.value === "custom" ? customDenom : selectedDenom.value
- ).toLowerCase();
- const exponent =
- state.chain.assets?.find(({ denom: currentDenom }) => currentDenom.toLowerCase() === denom)
- ?.exponent ?? 0;
+ const denom =
+ selectedDenom.value === customDenomOption.value ? customDenom : selectedDenom.value.symbol;
const amountInAtomics = (() => {
try {
+ if (selectedDenom.value === customDenomOption.value) {
+ return Decimal.fromUserInput(amount, 0).atomics;
+ }
+
+ const foundAsset = state.chain.assets?.find((asset) => asset.symbol === denom);
+ const exponent =
+ foundAsset?.denom_units.find((unit) => unit.denom === foundAsset.symbol.toLowerCase())
+ ?.exponent ?? 0;
+
return Decimal.fromUserInput(amount, exponent).atomics;
} catch {
return "0";
@@ -155,7 +143,7 @@ const MsgSendForm = ({ fromAddress, setMsgGetter, deleteMsg }: MsgSendFormProps)
value={selectedDenom}
onChange={(option: (typeof denomOptions)[number]) => {
setSelectedDenom(option);
- if (option.value !== "custom") {
+ if (option.value !== customDenomOption.value) {
setCustomDenom("");
}
}}
@@ -168,9 +156,11 @@ const MsgSendForm = ({ fromAddress, setMsgGetter, deleteMsg }: MsgSendFormProps)
value={customDenom}
onChange={({ target }) => setCustomDenom(target.value)}
placeholder={
- selectedDenom.value === "custom" ? "Enter custom denom" : "Select Custom denom above"
+ selectedDenom.value === customDenomOption.value
+ ? "Enter custom denom"
+ : "Select Custom denom above"
}
- disabled={selectedDenom.value !== "custom"}
+ disabled={selectedDenom.value !== customDenomOption.value}
error={customDenomError}
/>
From b8fa36177916d45ee9f92e84def974ab4acfa7b1 Mon Sep 17 00:00:00 2001
From: abefernan <44572727+abefernan@users.noreply.github.com>
Date: Mon, 26 Jun 2023 11:59:01 +0200
Subject: [PATCH 11/13] Assert min assets
---
components/chainSelect/ChainSelect.tsx | 1 +
1 file changed, 1 insertion(+)
diff --git a/components/chainSelect/ChainSelect.tsx b/components/chainSelect/ChainSelect.tsx
index d9aa6ba..93abf97 100644
--- a/components/chainSelect/ChainSelect.tsx
+++ b/components/chainSelect/ChainSelect.tsx
@@ -136,6 +136,7 @@ const ChainSelect = () => {
try {
const chainData = await getChainFromRegistry(chainOption.path);
const registryAssets = await getAssetsFromRegistry(chainOption.path);
+ assert(registryAssets.length >= 1, "No assets found in registry");
const firstAsset = registryAssets[0];
const nodeAddress = await getNodeFromArray(chainData.apis.rpc);
From 0f16dc6652461757be3666ba785a6307be6ba52f Mon Sep 17 00:00:00 2001
From: abefernan <44572727+abefernan@users.noreply.github.com>
Date: Mon, 26 Jun 2023 11:59:37 +0200
Subject: [PATCH 12/13] Apply uppercase to ibc hash only
---
lib/displayHelpers.spec.ts | 4 ++--
lib/displayHelpers.ts | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/lib/displayHelpers.spec.ts b/lib/displayHelpers.spec.ts
index 25e00e9..3766e65 100644
--- a/lib/displayHelpers.spec.ts
+++ b/lib/displayHelpers.spec.ts
@@ -66,7 +66,7 @@ describe("printableCoin", () => {
});
it("works with IBC coin", () => {
- expect(printableCoin(ibcCoin, testChainInfo)).toEqual(`4000000${thinSpace}IBC/C4CFF…319F9`);
+ expect(printableCoin(ibcCoin, testChainInfo)).toEqual(`4000000${thinSpace}ibc/C4CFF…319F9`);
});
});
@@ -89,7 +89,7 @@ describe("printableCoins", () => {
];
expect(printableCoins(coins, testChainInfo)).toEqual(
- `0.001${thinSpace}JUNOX, 20000${thinSpace}UTEST, 300000${thinSpace}TEST, 4000000 IBC/C4CFF…319F9`,
+ `0.001${thinSpace}JUNOX, 20000${thinSpace}UTEST, 300000${thinSpace}TEST, 4000000 ibc/C4CFF…319F9`,
);
});
});
diff --git a/lib/displayHelpers.ts b/lib/displayHelpers.ts
index a635ead..100bf61 100644
--- a/lib/displayHelpers.ts
+++ b/lib/displayHelpers.ts
@@ -42,7 +42,7 @@ const printableCoin = (coin: Coin, chainInfo: ChainInfo) => {
const value = coin.amount;
const hash = coin.denom.slice(4);
const ellidedHash = ellideMiddle(hash, 11);
- const ticker = `ibc/${ellidedHash}`.toUpperCase();
+ const ticker = `ibc/${ellidedHash.toUpperCase()}`;
return value + thinSpace + ticker;
}
From b6bbcd122947a2dda2976da4205a2ab1fad48f2c Mon Sep 17 00:00:00 2001
From: abefernan <44572727+abefernan@users.noreply.github.com>
Date: Mon, 26 Jun 2023 11:59:43 +0200
Subject: [PATCH 13/13] Apply prettier
---
lib/displayHelpers.spec.ts | 42 ++++++++++++++++++++------------------
1 file changed, 22 insertions(+), 20 deletions(-)
diff --git a/lib/displayHelpers.spec.ts b/lib/displayHelpers.spec.ts
index 3766e65..9f04922 100644
--- a/lib/displayHelpers.spec.ts
+++ b/lib/displayHelpers.spec.ts
@@ -12,28 +12,30 @@ const testChainInfo: ChainInfo = {
denom: "ujunox",
displayDenom: "JUNOX",
displayDenomExponent: 6,
- assets: [{
- description: "The native token of JUNO Chain",
- denom_units: [
- {
- denom: "ujunox",
- exponent: 0
+ assets: [
+ {
+ description: "The native token of JUNO Chain",
+ denom_units: [
+ {
+ denom: "ujunox",
+ exponent: 0,
+ },
+ {
+ denom: "junox",
+ exponent: 6,
+ },
+ ],
+ base: "ujunox",
+ name: "Juno Testnet",
+ display: "junox",
+ symbol: "JUNOX",
+ logo_URIs: {
+ png: "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/junotestnet/images/juno.png",
+ svg: "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/junotestnet/images/juno.svg",
},
- {
- denom: "junox",
- exponent: 6
- }
- ],
- base: "ujunox",
- name: "Juno Testnet",
- display: "junox",
- symbol: "JUNOX",
- logo_URIs: {
- png: "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/junotestnet/images/juno.png",
- svg: "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/junotestnet/images/juno.svg"
+ coingecko_id: "juno-network",
},
- coingecko_id: "juno-network"
- }],
+ ],
gasPrice: "0.04ujunox",
};