Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[APIS-313] Support ethermint ledger sign(cosmos) #346

Draft
wants to merge 33 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
d920eb6
feat: Support connect Ethermint(60' coin type path) chain with ledger
G-Gamja May 17, 2024
40bf857
feat: Add support for CosSignEIP712 message type
G-Gamja May 21, 2024
9c37724
feat: Add cos_signEIP712 api
G-Gamja May 22, 2024
6064e12
Merge branch 'develop' of https://github.com/cosmostation/cosmostatio…
G-Gamja May 22, 2024
0126c0e
Remove ethermintPublicKey from LedgerAccount type
G-Gamja May 22, 2024
3a2e0db
Refactor ethermint util funcs
G-Gamja May 22, 2024
95217f8
Block Ethermint like chain inApp features
G-Gamja May 22, 2024
bd80d46
chore: Update translation for insufficient fee amount
G-Gamja May 22, 2024
1a6ece0
feat: Update LedgerPublicKeyRequest to support multiple chain lines
G-Gamja May 23, 2024
4efcb90
Refactor ethermint util funcs
G-Gamja May 23, 2024
8dfc914
feat: 인앱 이더민트 사인 구현
G-Gamja May 24, 2024
97737af
feat: Support ethermint ledger sign
G-Gamja May 28, 2024
f0ee186
ethermintPK, ethereumPK분리
G-Gamja May 28, 2024
7d61d5d
feat: Update LEDGER_SUPPORT_COIN_TYPE for ethermint support
G-Gamja May 28, 2024
2a90f85
feat: Support in-app ethermint ibc ledger sign
G-Gamja May 29, 2024
3def36c
Merge branch 'develop' of https://github.com/cosmostation/cosmostatio…
G-Gamja May 30, 2024
9ff4c9d
feat: Develop ethermint ledger signing
G-Gamja May 31, 2024
a056d2c
feat:export amino sign callback methods
G-Gamja May 31, 2024
0c5ceab
refactor((cosmos sign page)): edit fee coin selection logic
G-Gamja Jun 7, 2024
7f9baa4
chore: Update gas rate calculation logic in Cosmos sign pages
G-Gamja Jun 10, 2024
34aeea4
refactor(cosmos sign page): delete meaningless using spread operator
G-Gamja Jun 10, 2024
1095f31
refactor: Edit fee coin selection logic in Cosmos sign pages
G-Gamja Jun 10, 2024
7f1f9e3
feat: Add Long support for big numbers
G-Gamja Jun 11, 2024
bf6a6d4
refactor: remove meaningless code changes
G-Gamja Jun 11, 2024
2ac1701
refactor: remove empty space
G-Gamja Jun 11, 2024
dd35aa7
fix: Checking unknown fee coin balance
G-Gamja Jun 13, 2024
e03307d
feat: Remove Ledger support for Ethermint-like chains
G-Gamja Jun 18, 2024
4e07281
refactor: remove seperate execute sign func
G-Gamja Jun 21, 2024
9302be5
feat: Improve Cosmos sign entry page
G-Gamja Jun 24, 2024
4260170
Merge branch 'develop' of https://github.com/cosmostation/cosmostatio…
G-Gamja Jun 24, 2024
d33dd14
Merge branch 'feature/APIS-343-fix-cosmos-sign-page-fee-selecting' of…
G-Gamja Jun 24, 2024
1f949a6
refactor: expand isInjectiveChain condition(allow testnet)
G-Gamja Jun 24, 2024
b14edb8
Merge branch 'develop' of https://github.com/cosmostation/cosmostatio…
G-Gamja Jun 27, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
"js-yaml": "^4.1.0",
"ledger-cosmos-js": "^2.1.8",
"lodash": "^4.17.21",
"long": "^5.2.3",
"notistack": "^2.0.4",
"protobufjs": "^6.11.2",
"qrcode.react": "^3.0.1",
Expand Down
2 changes: 2 additions & 0 deletions src/Popup/Routes/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import PopupCosmosAddNFTs from '~/Popup/pages/Popup/Cosmos/AddNFTs';
import PopupCosmosAddTokens from '~/Popup/pages/Popup/Cosmos/AddTokens';
import PopupCosmosSignAmino from '~/Popup/pages/Popup/Cosmos/Sign/Amino';
import PopupCosmosSignDirect from '~/Popup/pages/Popup/Cosmos/Sign/Direct';
import PopupCosmosSignEIP712 from '~/Popup/pages/Popup/Cosmos/Sign/EIP712';
import PopupCosmosSignMessage from '~/Popup/pages/Popup/Cosmos/Sign/Message';
import PopupEthereumAddNetwork from '~/Popup/pages/Popup/Ethereum/AddNetwork';
import PopupEthereumAddTokens from '~/Popup/pages/Popup/Ethereum/AddTokens';
Expand Down Expand Up @@ -164,6 +165,7 @@ export default function Routes() {
<Route path={PATH.POPUP__COSMOS__SIGN__AMINO} element={<PopupCosmosSignAmino />} />
<Route path={PATH.POPUP__COSMOS__SIGN__DIRECT} element={<PopupCosmosSignDirect />} />
<Route path={PATH.POPUP__COSMOS__SIGN__MESSAGE} element={<PopupCosmosSignMessage />} />
<Route path={PATH.POPUP__COSMOS__SIGN__EIP712} element={<PopupCosmosSignEIP712 />} />

<Route path={PATH.POPUP__APTOS__TRANSACTION} element={<PopupAptosTransaction />} />
<Route path={PATH.POPUP__APTOS__SIGN_MESSAGE} element={<PopupAptosSignMessage />} />
Expand Down
95 changes: 95 additions & 0 deletions src/Popup/background/joiSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { ethereumAddressRegex, getCosmosAddressRegex } from '~/Popup/utils/regex
import type { CosmosChain, GasRate } from '~/types/chain';
import type { Fee, Msg, SignAminoDoc } from '~/types/cosmos/amino';
import type { Amount } from '~/types/cosmos/common';
import type { EIP712StructuredData } from '~/types/cosmos/ethermint';
import type { SignDirectDoc } from '~/types/cosmos/proto';
import type { AptosSignMessage, AptosSignTransaction } from '~/types/message/aptos';
import type {
Expand All @@ -17,6 +18,7 @@ import type {
CosSendTransaction,
CosSignAmino,
CosSignDirect,
CosSignEIP712,
CosSignMessage,
CosVerifyMessage,
} from '~/types/message/cosmos';
Expand Down Expand Up @@ -102,6 +104,7 @@ export const cosSignAminoParamsSchema = (chainNames: string[], chainId: string)
gas: Joi.string().required(),
payer: Joi.string().optional(),
granter: Joi.string().optional(),
feePayer: Joi.string().optional(),
}),
memo: Joi.string().allow(''),
msgs: Joi.array().items(
Expand Down Expand Up @@ -137,6 +140,98 @@ export const cosSignMessageParamsSchema = (chainNames: string[]) =>
.label('params')
.required();

export const cosSignEIP712ParamsSchema = (chainId: string) => {
const chainIdRegex = getChainIdRegex(chainId);

return Joi.object<CosSignEIP712['params']>({
chainId: Joi.string().trim().pattern(chainIdRegex).required(),
signer: Joi.string().required(),
eip712: Joi.object<EIP712StructuredData>({
types: Joi.object({
EIP712Domain: Joi.array()
.items(
Joi.object<{
name: string;
type: string;
}>({
name: Joi.string().valid('name').required(),
type: Joi.string().valid('string').required(),
}),
Joi.object<{
name: string;
type: string;
}>({
name: Joi.string().valid('version').required(),
type: Joi.string().valid('string').required(),
}),
Joi.object<{
name: string;
type: string;
}>({
name: Joi.string().valid('chainId').required(),
type: Joi.string().valid('uint256').required(),
}),
Joi.object<{
name: string;
type: string;
}>({
name: Joi.string().valid('verifyingContract').required(),
type: Joi.string().valid('address', 'string').required(),
}),
Joi.object<{
name: string;
type: string;
}>({
name: Joi.string().valid('salt').required(),
type: Joi.string().valid('bytes32', 'string').required(),
}),
)
.unique()
.min(1)
.custom((value) => {
const domainFieldNames: Array<string> = ['name', 'version', 'chainId', 'verifyingContract', 'salt'];

// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access
return value.sort((a: { name: string }, b: { name: string }) => domainFieldNames.indexOf(a.name) - domainFieldNames.indexOf(b.name));
})
.required(),
})
.unknown(true)
.required(),
domain: Joi.object().required(),
primaryType: Joi.string().required(),
})
.unknown(true)
.required(),
doc: Joi.object<SignAminoDoc>({
chain_id: Joi.string().trim().pattern(chainIdRegex).required(),
sequence: Joi.string().required(),
account_number: Joi.string().required(),
fee: Joi.object<Fee>({
amount: Joi.array()
.items(Joi.object<Amount>({ amount: Joi.string().required(), denom: Joi.string().required() }))
.optional(),
gas: Joi.string().required(),
payer: Joi.string().optional(),
feePayer: Joi.string().optional(),
granter: Joi.string().optional(),
}),
memo: Joi.string().allow(''),
msgs: Joi.array().items(
Joi.object<Msg>({
type: Joi.string().required(),
value: Joi.any(),
}),
),
timeout_height: Joi.string().optional(),
}).required(),
isEditFee: Joi.boolean().default(true),
isEditMemo: Joi.boolean().default(false),
isCheckBalance: Joi.boolean().default(true),
})
.label('params')
.required();
};
export const cosVerifyMessageParamsSchema = (chainNames: string[]) =>
Joi.object<CosVerifyMessage['params']>({
chainName: Joi.string()
Expand Down
59 changes: 55 additions & 4 deletions src/Popup/background/messageProcessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ import type {
CosSendTransactionResponse,
CosSignAmino,
CosSignDirect,
CosSignEIP712,
CosSignMessage,
CosSupportedChainIdsResponse,
CosSupportedChainNamesResponse,
Expand Down Expand Up @@ -100,6 +101,7 @@ import {
cosSendTransactionParamsSchema,
cosSignAminoParamsSchema,
cosSignDirectParamsSchema,
cosSignEIP712ParamsSchema,
cosSignMessageParamsSchema,
cosVerifyMessageParamsSchema,
ethcAddNetworkParamsSchema,
Expand Down Expand Up @@ -238,7 +240,15 @@ export async function cstob(request: ContentScriptToBackgroundEventMessage<Reque

const chain = getChain(chainName)!;

if (currentAccount.type === 'LEDGER' && chain.bip44.coinType !== LEDGER_SUPPORT_COIN_TYPE.COSMOS) {
if (
currentAccount.type === 'LEDGER' &&
![
LEDGER_SUPPORT_COIN_TYPE.COSMOS,
LEDGER_SUPPORT_COIN_TYPE.ETHERMINT,
LEDGER_SUPPORT_COIN_TYPE.MEDIBLOC,
LEDGER_SUPPORT_COIN_TYPE.CRONOS_POS,
].includes(chain.bip44.coinType)
) {
throw new CosmosRPCError(RPC_ERROR.LEDGER_UNSUPPORTED_CHAIN, COSMOS_RPC_ERROR_MESSAGE[RPC_ERROR.LEDGER_UNSUPPORTED_CHAIN]);
}

Expand Down Expand Up @@ -331,7 +341,10 @@ export async function cstob(request: ContentScriptToBackgroundEventMessage<Reque

const schema = cosSignAminoParamsSchema(allChainLowercaseNames, chain ? chain.chainId : '');

if (currentAccount.type === 'LEDGER' && chain?.bip44.coinType !== LEDGER_SUPPORT_COIN_TYPE.COSMOS) {
if (
currentAccount.type === 'LEDGER' &&
![LEDGER_SUPPORT_COIN_TYPE.COSMOS, LEDGER_SUPPORT_COIN_TYPE.MEDIBLOC, LEDGER_SUPPORT_COIN_TYPE.CRONOS_POS].includes(chain?.bip44.coinType || '')
) {
throw new CosmosRPCError(RPC_ERROR.LEDGER_UNSUPPORTED_CHAIN, COSMOS_RPC_ERROR_MESSAGE[RPC_ERROR.LEDGER_UNSUPPORTED_CHAIN]);
}

Expand Down Expand Up @@ -376,6 +389,33 @@ export async function cstob(request: ContentScriptToBackgroundEventMessage<Reque
}
}

if (method === 'cos_signEIP712') {
if (currentAccount.type !== 'LEDGER') {
throw new CosmosRPCError(RPC_ERROR.LEDGER_SUPPORTED_METHOD, COSMOS_RPC_ERROR_MESSAGE[RPC_ERROR.LEDGER_SUPPORTED_METHOD]);
}
const { params } = message;

const selectedChain = allChains.find((item) => item.chainId === params.chainId);

const schema = cosSignEIP712ParamsSchema(selectedChain ? selectedChain.chainId : '');

if (currentAccount.type === 'LEDGER' && selectedChain?.bip44.coinType !== LEDGER_SUPPORT_COIN_TYPE.ETHERMINT) {
throw new CosmosRPCError(RPC_ERROR.LEDGER_UNSUPPORTED_CHAIN, COSMOS_RPC_ERROR_MESSAGE[RPC_ERROR.LEDGER_UNSUPPORTED_CHAIN]);
}

try {
const validatedParams = (await schema.validateAsync({ ...params })) as CosSignEIP712['params'];

localQueues.push({
...request,
message: { ...request.message, method, params: { ...validatedParams } as CosSignEIP712['params'] },
});
void setQueues();
} catch (err) {
throw new CosmosRPCError(RPC_ERROR.INVALID_PARAMS, `${err as string}`);
}
}

if (method === 'cos_signMessage') {
const { params } = message;

Expand All @@ -391,7 +431,10 @@ export async function cstob(request: ContentScriptToBackgroundEventMessage<Reque

const schema = cosSignMessageParamsSchema(allChainLowercaseNames);

if (currentAccount.type === 'LEDGER' && chain?.bip44.coinType !== LEDGER_SUPPORT_COIN_TYPE.COSMOS) {
if (
currentAccount.type === 'LEDGER' &&
![LEDGER_SUPPORT_COIN_TYPE.COSMOS, LEDGER_SUPPORT_COIN_TYPE.MEDIBLOC, LEDGER_SUPPORT_COIN_TYPE.CRONOS_POS].includes(chain.bip44.coinType)
) {
throw new CosmosRPCError(RPC_ERROR.LEDGER_UNSUPPORTED_CHAIN, COSMOS_RPC_ERROR_MESSAGE[RPC_ERROR.LEDGER_UNSUPPORTED_CHAIN]);
}

Expand Down Expand Up @@ -614,7 +657,15 @@ export async function cstob(request: ContentScriptToBackgroundEventMessage<Reque

const chain = getChain(chainName);

if (currentAccount.type === 'LEDGER' && chain?.bip44.coinType !== LEDGER_SUPPORT_COIN_TYPE.COSMOS) {
if (
currentAccount.type === 'LEDGER' &&
![
LEDGER_SUPPORT_COIN_TYPE.COSMOS,
LEDGER_SUPPORT_COIN_TYPE.ETHERMINT,
LEDGER_SUPPORT_COIN_TYPE.MEDIBLOC,
LEDGER_SUPPORT_COIN_TYPE.CRONOS_POS,
].includes(chain?.bip44.coinType || '')
) {
throw new CosmosRPCError(RPC_ERROR.LEDGER_UNSUPPORTED_CHAIN, COSMOS_RPC_ERROR_MESSAGE[RPC_ERROR.LEDGER_UNSUPPORTED_CHAIN]);
}

Expand Down
4 changes: 4 additions & 0 deletions src/Popup/components/Wrapper/Routes/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ export default function Routes({ children }: RoutesType) {
navigate('/popup/cosmos/sign/message');
}

if (extensionStorage.queues[0].message.method === 'cos_signEIP712') {
navigate('/popup/cosmos/sign/eip712');
}

if (extensionStorage.queues[0].message.method === 'cos_addTokensCW20Internal') {
navigate('/popup/cosmos/add-tokens');
}
Expand Down
22 changes: 17 additions & 5 deletions src/Popup/components/requests/LedgerPublicKeyRequest/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useMemo } from 'react';
import { useSnackbar } from 'notistack';
import TinySecp256k1 from 'tiny-secp256k1';
import EthereumApp from '@ledgerhq/hw-app-eth';
import { Typography } from '@mui/material';
import Sui from '@mysten/ledgerjs-hw-app-sui';
Expand Down Expand Up @@ -92,22 +93,25 @@ export default function LedgerPublicKeyRequest({ children }: AccessRequestProps)

if (currentAccount.type === 'LEDGER' && chain && currentQueue) {
if (
![LEDGER_SUPPORT_COIN_TYPE.COSMOS, LEDGER_SUPPORT_COIN_TYPE.MEDIBLOC, LEDGER_SUPPORT_COIN_TYPE.CRONOS_POS].includes(chain.bip44.coinType) &&
![LEDGER_SUPPORT_COIN_TYPE.COSMOS, LEDGER_SUPPORT_COIN_TYPE.ETHERMINT, LEDGER_SUPPORT_COIN_TYPE.MEDIBLOC, LEDGER_SUPPORT_COIN_TYPE.CRONOS_POS].includes(
chain.bip44.coinType,
) &&
chain.line === 'COSMOS'
) {
return null;
}

if (
(!currentAccount.cosmosPublicKey && chain.bip44.coinType === LEDGER_SUPPORT_COIN_TYPE.COSMOS && chain.line === 'COSMOS') ||
(!currentAccount.ethermintPublicKey && chain.bip44.coinType === LEDGER_SUPPORT_COIN_TYPE.ETHERMINT && chain.line === 'COSMOS') ||
(!currentAccount.mediblocPublicKey && chain.bip44.coinType === LEDGER_SUPPORT_COIN_TYPE.MEDIBLOC && chain.line === 'COSMOS') ||
(!currentAccount.cryptoOrgPublicKey && chain.bip44.coinType === LEDGER_SUPPORT_COIN_TYPE.CRONOS_POS && chain.line === 'COSMOS') ||
(!currentAccount.ethereumPublicKey && chain.bip44.coinType === LEDGER_SUPPORT_COIN_TYPE.ETHEREUM && chain.line === 'ETHEREUM') ||
(!currentAccount.suiPublicKey && chain.bip44.coinType === LEDGER_SUPPORT_COIN_TYPE.SUI && chain.line === 'SUI')
) {
const Step2 = (() => {
if (chain.bip44.coinType === LEDGER_SUPPORT_COIN_TYPE.COSMOS) return Step2CosmosIcon;
if (chain.bip44.coinType === LEDGER_SUPPORT_COIN_TYPE.ETHEREUM) return Step2EthereumIcon;
if (chain.bip44.coinType === LEDGER_SUPPORT_COIN_TYPE.ETHEREUM || chain.bip44.coinType === LEDGER_SUPPORT_COIN_TYPE.ETHERMINT) return Step2EthereumIcon;
if (chain.bip44.coinType === LEDGER_SUPPORT_COIN_TYPE.MEDIBLOC) return Step2Medibloc;
if (chain.bip44.coinType === LEDGER_SUPPORT_COIN_TYPE.CRONOS_POS) return Step2CryptoOrg;
if (chain.bip44.coinType === LEDGER_SUPPORT_COIN_TYPE.SUI) return Step2Sui;
Expand All @@ -116,7 +120,7 @@ export default function LedgerPublicKeyRequest({ children }: AccessRequestProps)

const appName = (() => {
if (chain.bip44.coinType === LEDGER_SUPPORT_COIN_TYPE.COSMOS) return 'Cosmos';
if (chain.bip44.coinType === LEDGER_SUPPORT_COIN_TYPE.ETHEREUM) return 'Ethereum';
if (chain.bip44.coinType === LEDGER_SUPPORT_COIN_TYPE.ETHEREUM || chain.bip44.coinType === LEDGER_SUPPORT_COIN_TYPE.ETHERMINT) return 'Ethereum';
if (chain.bip44.coinType === LEDGER_SUPPORT_COIN_TYPE.MEDIBLOC) return 'Medibloc';
if (chain.bip44.coinType === LEDGER_SUPPORT_COIN_TYPE.CRONOS_POS) return 'Cronos POS';
if (chain.bip44.coinType === LEDGER_SUPPORT_COIN_TYPE.SUI) return 'Sui';
Expand Down Expand Up @@ -236,7 +240,10 @@ export default function LedgerPublicKeyRequest({ children }: AccessRequestProps)
}
}

if (chain.bip44.coinType === LEDGER_SUPPORT_COIN_TYPE.ETHEREUM) {
if (
chain.bip44.coinType === LEDGER_SUPPORT_COIN_TYPE.ETHEREUM ||
(chain.bip44.coinType === LEDGER_SUPPORT_COIN_TYPE.ETHERMINT && chain.line === 'COSMOS')
) {
const ethereumApp = new EthereumApp(transport);

const path = `${chain.bip44.purpose}/${chain.bip44.coinType}/${chain.bip44.account}/${chain.bip44.change}/${currentAccount.bip44.addressIndex}`;
Expand All @@ -245,8 +252,13 @@ export default function LedgerPublicKeyRequest({ children }: AccessRequestProps)
const { publicKey } = result;

if (accountIndex > -1) {
newAccounts.splice(accountIndex, 1, { ...currentAccount, ethereumPublicKey: publicKey });
if (chain.line === 'COSMOS') {
const compressedPublicKey = Buffer.from(TinySecp256k1.pointCompress(Buffer.from(publicKey, 'hex'), true)).toString('hex');

newAccounts.splice(accountIndex, 1, { ...currentAccount, ethermintPublicKey: compressedPublicKey });
} else {
newAccounts.splice(accountIndex, 1, { ...currentAccount, ethereumPublicKey: publicKey });
}
await setExtensionStorage('accounts', newAccounts);
}
}
Expand Down
19 changes: 19 additions & 0 deletions src/Popup/hooks/useEthermintLedgerSign.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { useMemo } from 'react';

import { LEDGER_SUPPORT_COIN_TYPE } from '~/constants/ledger';
import type { CosmosChain } from '~/types/chain';

import { useCurrentAccount } from './useCurrent/useCurrentAccount';

export function useEthermintLedgerSign(chain: CosmosChain) {
const { currentAccount } = useCurrentAccount();

const isLedgerAccount = useMemo(() => currentAccount.type === 'LEDGER', [currentAccount.type]);
const isEthermintChain = useMemo(() => chain.bip44.coinType === LEDGER_SUPPORT_COIN_TYPE.ETHERMINT, [chain.bip44.coinType]);

const isEthermintLedgerSign = useMemo(() => isLedgerAccount && isEthermintChain, [isEthermintChain, isLedgerAccount]);

const isInjectiveChain = useMemo(() => chain.id.startsWith('injective'), [chain.id]);

return { isEthermintLedgerSign, isInjectiveChain };
}
11 changes: 11 additions & 0 deletions src/Popup/i18n/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -782,6 +782,7 @@
"detailTab": "Details",
"failedTransfer": "Failed to transfer",
"insufficientFeeAmount": "Insufficient fee",
"notOpendEthApp": "Please open the Ethereum application on your Ledger device and try again.",
"signButton": "Sign"
}
},
Expand Down Expand Up @@ -825,6 +826,15 @@
"invalidAccountType": "Invalid account type (Ledger)"
}
},
"EIP712": {
"entry": {
"cancelButton": "Cancel",
"dataTab": "Data",
"detailTab": "Details",
"insufficientFeeAmount": "Insufficient fee",
"signButton": "Sign"
}
},
"Message": {
"entry": {
"cancelButton": "Cancel",
Expand Down Expand Up @@ -1616,6 +1626,7 @@
"invalidAmount": "Invalid amount",
"invalidOutputAmount": "Invalid output amount",
"invalidSwapTx": "Invalid tx data",
"ledgerNotSupported": "Not support signing with Ledger on Ethermint-like chains",
"lessThanFeeWarningDescription1": "You do not have enough balance to cover the estimated gas costs for this transaction. Make sure you have more than",
"lessThanFeeWarningDescription2": "before trying again.",
"liquidityWarning": "The size of this trade is large compared to the available liquidity. Reduce the swap amount to get a better price.",
Expand Down
Loading