diff --git a/CHANGELOG.md b/CHANGELOG.md index f6fa35cc..d14b5953 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,16 @@ # Changelog +## `v0.0.48` + +### Features + +- Added Compass wallet support to execute txs via Compass extension on Sei network (*does not support wallet connect yet*) + ## `v0.0.47` ### Features -- Added `MetamaskInjective` wallet to execute txs via MetaMask extension on Injective network (*does not support wallet connect yet*) +- Added MetaMask wallet support to execute txs via MetaMask extension on Injective network (*does not support wallet connect yet*) ### Improvements diff --git a/README.md b/README.md index 17fe51eb..8c17eb62 100644 --- a/README.md +++ b/README.md @@ -131,6 +131,7 @@ This directory is a [Cosmos Kit](https://cosmoskit.com) alternative to interact - [Keplr](https://www.keplr.app/) - [Leap](https://www.leapwallet.io/) - [Cosmostation](https://wallet.cosmostation.io/) +- [Compass](https://compasswallet.io/) (for Sei only) - [MetaMask](https://metamask.io/) (for Injective only) **Features**: diff --git a/examples/solid-vite/src/App.tsx b/examples/solid-vite/src/App.tsx index 931f99e6..f8a5573a 100644 --- a/examples/solid-vite/src/App.tsx +++ b/examples/solid-vite/src/App.tsx @@ -3,6 +3,7 @@ import { createStore } from "solid-js/store"; import { MsgSend } from "cosmes/client"; import { + CompassController, ConnectedWallet, CosmostationController, KeplrController, @@ -17,7 +18,7 @@ import { const WC_PROJECT_ID = "2b7d5a2da89dd74fed821d184acabf95"; const SIGN_ARBITRARY_MSG = - "Hi from Coinhall! This is a test message just to prove that the wallet is working."; + "Hi from CosmeES! This is a test message just to prove that the wallet is working."; const TX_MEMO = "signed via cosmes"; const CHAINS: Record = { @@ -29,12 +30,14 @@ const CHAINS: Record = { "neutron-1": "Neutron", "migaloo-1": "Migaloo", "injective-1": "Injective", + "pacific-1": "Sei", }; const WALLETS: Record = { [WalletName.KEPLR]: "Keplr", [WalletName.COSMOSTATION]: "Cosmostation", [WalletName.STATION]: "Terra Station", [WalletName.LEAP]: "Leap", + [WalletName.COMPASS]: "Compass", [WalletName.METAMASK_INJECTIVE]: "MetaMask", }; const TYPES: Record = { @@ -45,6 +48,7 @@ const CONTROLLERS: Record = { [WalletName.STATION]: new StationController(), [WalletName.KEPLR]: new KeplrController(WC_PROJECT_ID), [WalletName.LEAP]: new LeapController(WC_PROJECT_ID), + [WalletName.COMPASS]: new CompassController(), [WalletName.COSMOSTATION]: new CosmostationController(WC_PROJECT_ID), [WalletName.METAMASK_INJECTIVE]: new MetamaskInjectiveController(), }; @@ -67,6 +71,8 @@ function getRpc(chain: string): string { return "https://migaloo-rpc.polkachu.com"; case "injective-1": return "https://injective-rpc.polkachu.com"; + case "pacific-1": + return "https://rpc-sei-ia.cosmosia.notional.ventures"; default: throw new Error("Unknown chain"); } @@ -90,6 +96,8 @@ function getGasPrice(chain: string): { amount: string; denom: string } { return { amount: "0.25", denom: getDenom(chain) }; case "injective-1": return { amount: "500000000", denom: getDenom(chain) }; + case "pacific-1": + return { amount: "0.1", denom: getDenom(chain) }; default: throw new Error("Unknown chain"); } @@ -112,16 +120,16 @@ function getDenom(chain: string): string { return "uwhale"; case "injective-1": return "inj"; + case "pacific-1": + return "usei"; default: throw new Error("Unknown chain"); } } const App: Component = () => { - const [chain, setChain] = createSignal("injective-1"); - const [wallet, setWallet] = createSignal( - WalletName.METAMASK_INJECTIVE - ); + const [chain, setChain] = createSignal("pacific-1"); + const [wallet, setWallet] = createSignal(WalletName.COMPASS); const [wallets, setWallets] = createStore>( {} ); diff --git a/package.json b/package.json index 7b6b477f..38fdd052 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cosmes", - "version": "0.0.47", + "version": "0.0.48", "private": false, "packageManager": "pnpm@8.3.0", "sideEffects": false, diff --git a/src/wallet/constants/WalletName.ts b/src/wallet/constants/WalletName.ts index 7f270538..7eb55df7 100644 --- a/src/wallet/constants/WalletName.ts +++ b/src/wallet/constants/WalletName.ts @@ -5,6 +5,7 @@ export const WalletName = { STATION: "station", KEPLR: "keplr", LEAP: "leap", + COMPASS: "compass", COSMOSTATION: "cosmostation", METAMASK_INJECTIVE: "metamask-injective", } as const; diff --git a/src/wallet/index.ts b/src/wallet/index.ts index 6a8e9e2c..e4b60d46 100644 --- a/src/wallet/index.ts +++ b/src/wallet/index.ts @@ -12,6 +12,7 @@ export { type ChainInfo, type EventCallback, } from "./wallets/WalletController"; +export { CompassController } from "./wallets/compass/CompassController"; export { CosmostationController } from "./wallets/cosmostation/CosmostationController"; export { KeplrController } from "./wallets/keplr/KeplrController"; export { LeapController } from "./wallets/leap/LeapController"; diff --git a/src/wallet/wallets/WalletController.ts b/src/wallet/wallets/WalletController.ts index b73d0ea5..b4f15a2f 100644 --- a/src/wallet/wallets/WalletController.ts +++ b/src/wallet/wallets/WalletController.ts @@ -116,6 +116,10 @@ export abstract class WalletController { * `onAccountChange` event. */ protected changeAccount(walletType: WalletType) { + // Ignore if controller does not have any connected wallets + if (this.connectedWallets.size === 0) { + return; + } // Find all wallets that were connected via the given `walletType` const wallets = [...this.connectedWallets.values()].filter( (wallet) => wallet.type === walletType diff --git a/src/wallet/wallets/compass/CompassController.ts b/src/wallet/wallets/compass/CompassController.ts new file mode 100644 index 00000000..36f022e6 --- /dev/null +++ b/src/wallet/wallets/compass/CompassController.ts @@ -0,0 +1,77 @@ +import { Secp256k1PubKey } from "cosmes/client"; + +import { WalletName } from "../../constants/WalletName"; +import { WalletType } from "../../constants/WalletType"; +import { onWindowEvent } from "../../utils/window"; +import { WalletConnectV1 } from "../../walletconnect/WalletConnectV1"; +import { WalletConnectV2 } from "../../walletconnect/WalletConnectV2"; +import { ConnectedWallet } from "../ConnectedWallet"; +import { ChainInfo, WalletController } from "../WalletController"; +import { CompassExtension } from "./CompassExtension"; + +export class CompassController extends WalletController { + constructor() { + super(WalletName.COMPASS); + this.registerAccountChangeHandlers(); + } + + public async isInstalled(type: WalletType) { + return type === WalletType.EXTENSION ? "compass" in window : false; + } + + protected async connectWalletConnect( + _chains: ChainInfo[] + ): Promise<{ + wallets: Map; + wc: WalletConnectV1 | WalletConnectV2; + }> { + // Compass does not support WC yet + throw new Error("WalletConnect not supported"); + } + + protected async connectExtension(chains: ChainInfo[]) { + const wallets = new Map(); + const ext = window.compass; + if (!ext) { + throw new Error("Compass extension is not installed"); + } + await ext.enable(chains.map(({ chainId }) => chainId)); + for (const { chainId, rpc, gasPrice } of Object.values(chains)) { + const { bech32Address, pubKey, isNanoLedger } = await ext.getKey(chainId); + const key = new Secp256k1PubKey({ + key: pubKey, + }); + wallets.set( + chainId, + new CompassExtension( + this.id, + ext, + chainId, + key, + bech32Address, + rpc, + gasPrice, + isNanoLedger + ) + ); + } + return wallets; + } + + protected registerAccountChangeHandlers() { + /** + * ! IMPORTANT ! + * + * Since Leap also uses the same event key, this causes issues when a user + * has both leap and compass wallets connected simultaneously. For example, + * a change in Leap's keystore will trigger Compass to emit this event as + * well, leading to a race condition when `changeAccount` is called. + * + * The Compass team has been notified to possibly change this event emitted + * to `compass_keystorechange` instead. + */ + onWindowEvent("leap_keystorechange", () => + this.changeAccount(WalletType.EXTENSION) + ); + } +} diff --git a/src/wallet/wallets/compass/CompassExtension.ts b/src/wallet/wallets/compass/CompassExtension.ts new file mode 100644 index 00000000..b663f732 --- /dev/null +++ b/src/wallet/wallets/compass/CompassExtension.ts @@ -0,0 +1,4 @@ +import { LeapExtension } from "../leap/LeapExtension"; + +// Compass's API is similar to Leap. +export const CompassExtension = LeapExtension; diff --git a/src/wallet/wallets/compass/types.ts b/src/wallet/wallets/compass/types.ts new file mode 100644 index 00000000..46b575c0 --- /dev/null +++ b/src/wallet/wallets/compass/types.ts @@ -0,0 +1,8 @@ +import { Leap } from "../leap/types"; + +// Type is similar to Leap +export type Compass = Leap; + +export type Window = { + compass?: Compass | undefined; +}; diff --git a/src/wallet/wallets/window.d.ts b/src/wallet/wallets/window.d.ts index 57ca4b7a..8a4314bc 100644 --- a/src/wallet/wallets/window.d.ts +++ b/src/wallet/wallets/window.d.ts @@ -1,5 +1,6 @@ import { Window as KeplrWindow } from "cosmes/registry"; +import { Window as CompassWindow } from "./compass/types"; import { Window as CosmostationWindow } from "./cosmostation/types"; import { Window as LeapWindow } from "./leap/types"; import { Window as EthereumWindow } from "./metamask-injective/types"; @@ -11,5 +12,6 @@ declare global { CosmostationWindow, StationWindow, LeapWindow, + CompassWindow, EthereumWindow {} }