diff --git a/packages/core/src/types/wallet.ts b/packages/core/src/types/wallet.ts index f20941600..b2c7a915c 100644 --- a/packages/core/src/types/wallet.ts +++ b/packages/core/src/types/wallet.ts @@ -64,13 +64,13 @@ export interface Metadata { export interface AppUrl { native?: - | string - | { - android?: string; - ios?: string; - macos?: string; - windows?: string; - }; + | string + | { + android?: string; + ios?: string; + macos?: string; + windows?: string; + }; universal?: string; } @@ -82,14 +82,15 @@ export interface Wallet { mobileDisabled: boolean | (() => boolean); description?: string; rejectMessage?: - | { - source: string; // message from wallet app - target?: string; // message stored in walletManager, default 'Request Rejected!' - } - | string; // message from wallet app + | { + source: string; // message from wallet app + target?: string; // message stored in walletManager, default 'Request Rejected!' + } + | string; // message from wallet app rejectCode?: number; // code from wallet app connectEventNamesOnWindow?: string[]; connectEventNamesOnClient?: string[]; + supportedChains?: string[]; // array of supported chains downloads?: DownloadInfo[]; logo?: string | { major: string; minor: string }; walletconnect?: { @@ -244,7 +245,7 @@ export interface WalletClient { export type WalletAdapter = ChainWalletBase | MainWalletBase; export interface IChainWallet { - new (walletInfo: Wallet, chainInfo: ChainRecord): ChainWalletBase; + new(walletInfo: Wallet, chainInfo: ChainRecord): ChainWalletBase; } export type NameServiceName = string; diff --git a/packages/core/test-utils/mock-extension/extension/client.ts b/packages/core/test-utils/mock-extension/extension/client.ts index 21a47e3af..5892b757b 100644 --- a/packages/core/test-utils/mock-extension/extension/client.ts +++ b/packages/core/test-utils/mock-extension/extension/client.ts @@ -11,8 +11,8 @@ import { } from '@cosmos-kit/core'; import Long from 'long'; -import { Mock } from './types'; import { DirectSignDoc, WalletClient } from '../../../src/types'; +import { Mock } from './types'; export class MockClient implements WalletClient { readonly client: Mock; diff --git a/wallets/keplr-extension/package.json b/wallets/keplr-extension/package.json index 330e12f37..6fee9f274 100644 --- a/wallets/keplr-extension/package.json +++ b/wallets/keplr-extension/package.json @@ -71,7 +71,7 @@ ] }, "dependencies": { - "@chain-registry/keplr": "1.64.4", + "@chain-registry/keplr": "^1.68.2", "@cosmos-kit/core": "^2.12.0", "@keplr-wallet/provider-extension": "^0.12.95", "@keplr-wallet/types": "^0.12.95" @@ -81,4 +81,4 @@ "@cosmjs/proto-signing": ">=0.32.3" }, "gitHead": "2b5f2de5d9ed1580be4137736dfc6cce779679d1" -} +} \ No newline at end of file diff --git a/wallets/keplr-extension/src/extension/client.ts b/wallets/keplr-extension/src/extension/client.ts index a21225949..4a36ec771 100644 --- a/wallets/keplr-extension/src/extension/client.ts +++ b/wallets/keplr-extension/src/extension/client.ts @@ -14,6 +14,8 @@ import { import { BroadcastMode, Keplr } from '@keplr-wallet/types'; import Long from 'long'; +import { ExpiringLocalStorage } from './session'; + export class KeplrClient implements WalletClient { readonly client: Keplr; private _defaultSignOptions: SignOptions = { @@ -121,6 +123,20 @@ export class KeplrClient implements WalletClient { } async addChain(chainInfo: ChainRecord) { + // TODO later allow walletInfo getter to be available here + // make this more generic + const chainsAlreadyAdded = ExpiringLocalStorage.getItems('keplr/supported-chain') + if (chainsAlreadyAdded && chainsAlreadyAdded.length > 0) { + if (chainsAlreadyAdded.includes(chainInfo.name)) { + console.warn( + `${chainInfo.name} is already added. No need to call experimentalSuggestChain()` + ); + return; + } + } + + + const suggestChain = chainRegistryChainToKeplr( chainInfo.chain, chainInfo.assetList ? [chainInfo.assetList] : [] @@ -136,7 +152,15 @@ export class KeplrClient implements WalletClient { chainInfo.preferredEndpoints?.rpc?.[0]; } - await this.client.experimentalSuggestChain(suggestChain); + try { + await this.client.experimentalSuggestChain(suggestChain); + ExpiringLocalStorage.addItem('keplr/supported-chain', chainInfo.name, 1000 * 60) + } catch (error) { + console.log('Error while adding chain', error) + throw error + } + + } async signAmino( diff --git a/wallets/keplr-extension/src/extension/registry.ts b/wallets/keplr-extension/src/extension/registry.ts index 45ec2f089..e68dd4ce9 100644 --- a/wallets/keplr-extension/src/extension/registry.ts +++ b/wallets/keplr-extension/src/extension/registry.ts @@ -22,6 +22,44 @@ export const keplrExtensionInfo: Wallet = { source: 'Request rejected', }, connectEventNamesOnWindow: ['keplr_keystorechange'], + supportedChains: [ + 'agoric', + 'akash', + 'archway', + 'axelar', + 'bostrom', + 'celestia', + 'certik', + 'comdex', + 'cosmoshub', + 'cryptoorgchain', + 'dymension', + 'emoney', + 'evmos', + 'gravitybridge', + 'injective', + 'irisnet', + 'ixo', + 'juno', + 'kava', + 'kujira', + 'noble', + 'nois', + 'osmosis', + 'persistence', + 'quicksilver', + 'regen', + 'secretnetwork', + 'sentinel', + 'sifchain', + 'sommelier', + 'stargaze', + 'starname', + 'stride', + 'terra2', + 'tgrade', + 'umee', + ], downloads: [ { device: 'desktop', diff --git a/wallets/keplr-extension/src/extension/session.ts b/wallets/keplr-extension/src/extension/session.ts new file mode 100644 index 000000000..17388fe82 --- /dev/null +++ b/wallets/keplr-extension/src/extension/session.ts @@ -0,0 +1,94 @@ +export class ExpiringLocalStorage { + /** + * Add an item to an array in local storage with an expiration time. + * @param {string} key - The key under which the array is stored. + * @param {*} value - The value to be added. + * @param {number} ttl - Time to live in milliseconds. + */ + static addItem(key, value, ttl) { + const now = new Date(); + + // Create a new item with value and expiry + const newItem = { + value: value, + expiry: now.getTime() + ttl, + }; + + // Get existing items from local storage + const itemStr = localStorage.getItem(key); + const items = itemStr ? JSON.parse(itemStr) : []; + + // Add the new item to the array + items.push(newItem); + + // Store the updated array in local storage + localStorage.setItem(key, JSON.stringify(items)); + } + + /** + * Get all valid (non-expired) items from an array in local storage. + * @param {string} key - The key of the array to retrieve. + * @returns {Array} An array of valid items. + */ + static getItems(key) { + const itemStr = localStorage.getItem(key); + + // If the item doesn't exist, return an empty array + if (!itemStr) { + return []; + } + + const items = JSON.parse(itemStr); + const now = new Date(); + const validItems = []; + + // Filter out expired items + for (let item of items) { + if (now.getTime() <= item.expiry) { + validItems.push(item.value); + } + } + + // Update local storage with only valid items + localStorage.setItem(key, JSON.stringify(validItems.map(value => ({ + value, + expiry: items.find(item => item.value === value).expiry + })))); + + return validItems; + } + + /** + * Remove a specific item from the array in local storage. + * @param {string} key - The key of the array. + * @param {*} value - The value to be removed. + */ + static removeItem(key, value) { + const itemStr = localStorage.getItem(key); + + // If the item doesn't exist, return + if (!itemStr) { + return; + } + + let items = JSON.parse(itemStr); + + // Filter out the item to be removed + items = items.filter(item => item.value !== value); + + // Update local storage with the remaining items + localStorage.setItem(key, JSON.stringify(items)); + } +} + +// Usage example: + +// Add items with different TTLs +// ExpiringLocalStorage.addItem('myArray', 'item1', 10000); // Expires in 10 seconds +// ExpiringLocalStorage.addItem('myArray', 'item2', 20000); // Expires in 20 seconds + +// Get all valid items (non-expired) +// console.log(ExpiringLocalStorage.getItems('myArray')); + +// Remove a specific item +// ExpiringLocalStorage.removeItem('myArray', 'item1'); diff --git a/wallets/keplr-mobile/src/wallet-connect/registry.ts b/wallets/keplr-mobile/src/wallet-connect/registry.ts index dde7295ff..a81d38f4e 100644 --- a/wallets/keplr-mobile/src/wallet-connect/registry.ts +++ b/wallets/keplr-mobile/src/wallet-connect/registry.ts @@ -27,6 +27,46 @@ export const keplrMobileInfo: Wallet = { }, ], connectEventNamesOnWindow: ['keplr_keystorechange'], + supportedChains: [ + 'agoric', + 'akash', + 'axelar', + 'bitcanna', + 'bitsong', + 'bostrom', + 'certik', + 'cheqd', + 'chihuahua', + 'comdex', + 'cosmoshub', + 'cryptoorgchain', + 'desmos', + 'dig', + 'emoney', + 'evmos', + 'gravitybridge', + 'injective', + 'irisnet', + 'ixo', + 'juno', + 'ki', + 'likecoin', + 'lumnetwork', + 'osmosis', + 'panacea', + 'persistence', + 'regen', + 'secretnetwork', + 'sentinel', + 'sifchain', + 'sommelier', + 'stargaze', + 'starname', + 'terra', + 'tick', + 'umee', + 'vidulum', + ], walletconnect: { name: 'Keplr', projectId: diff --git a/yarn.lock b/yarn.lock index 6190b64f3..6b33a7b44 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1193,6 +1193,16 @@ "@keplr-wallet/crypto" "0.12.28" semver "^7.5.0" +"@chain-registry/keplr@1.68.2": + version "1.68.2" + resolved "https://registry.yarnpkg.com/@chain-registry/keplr/-/keplr-1.68.2.tgz#d322db5317e3c5299aa3cf62d1971403ed08fd31" + integrity sha512-H3rdf/cLx7bNyyKo+1nI9HpLTlLzyeqi0Rmt+ggwtFRC63ZmDaMg/3vPY4rHvu38OdcaOid4Nyfc+7h3EEPW8Q== + dependencies: + "@chain-registry/types" "^0.45.1" + "@keplr-wallet/cosmos" "0.12.28" + "@keplr-wallet/crypto" "0.12.28" + semver "^7.5.0" + "@chain-registry/keplr@^1.64.2": version "1.64.2" resolved "https://registry.yarnpkg.com/@chain-registry/keplr/-/keplr-1.64.2.tgz#254f392a8a2db285daa75dd849fb652e1973cf39" @@ -1213,6 +1223,11 @@ resolved "https://registry.yarnpkg.com/@chain-registry/types/-/types-0.41.1.tgz#0da38b579265456ded54d556f8da6a555babd086" integrity sha512-YZcmYjc1N4eOSd5Vi8rT1CtIDNvcqVoSNPBImfEqpBG+NBokO02WVQtsflFi4/MYRkLQQIHN7yDok09hld+c+Q== +"@chain-registry/types@^0.45.1": + version "0.45.1" + resolved "https://registry.yarnpkg.com/@chain-registry/types/-/types-0.45.1.tgz#bbe8cb65d80cf6be658f9eb0840eb72648d8cd08" + integrity sha512-xDq3RZwLM6VZt7Bwrilm588xTce7mOZIpLIjpwaT/V6HD3TuzJC3FWMRAxUtMuhQldcjW8b8em5HdFY467FRhA== + "@chain-registry/utils@^1.42.3": version "1.42.3" resolved "https://registry.yarnpkg.com/@chain-registry/utils/-/utils-1.42.3.tgz#90ad49d19bc095e991fa89bffaa70eaa8d9e8618"