diff --git a/wallets/react-wallet-v2/package.json b/wallets/react-wallet-v2/package.json index 7b36952..a3846b9 100644 --- a/wallets/react-wallet-v2/package.json +++ b/wallets/react-wallet-v2/package.json @@ -21,7 +21,8 @@ "framer-motion": "6.2.8", "ethers": "5.5.4", "valtio": "1.3.1", - "react-code-blocks": "0.0.9-0" + "react-code-blocks": "0.0.9-0", + "@cosmjs/proto-signing": "0.27.1" }, "devDependencies": { "@walletconnect/types": "2.0.0-beta.22", @@ -31,6 +32,6 @@ "eslint-config-next": "12.1.0", "eslint-config-prettier": "8.4.0", "prettier": "2.5.1", - "typescript": "4.5.5" + "typescript": "4.6.2" } } diff --git a/wallets/react-wallet-v2/src/components/Modal.tsx b/wallets/react-wallet-v2/src/components/Modal.tsx index ff2fdc9..7a2990c 100644 --- a/wallets/react-wallet-v2/src/components/Modal.tsx +++ b/wallets/react-wallet-v2/src/components/Modal.tsx @@ -1,6 +1,7 @@ import ModalStore from '@/store/ModalStore' import SessionProposalModal from '@/views/SessionProposalModal' import SessionSendTransactionModal from '@/views/SessionSendTransactionModal' +import SessionSignCosmosModal from '@/views/SessionSignCosmosModal' import SessionRequestModal from '@/views/SessionSignModal' import SessionSignTypedDataModal from '@/views/SessionSignTypedDataModal' import SessionUnsuportedMethodModal from '@/views/SessionUnsuportedMethodModal' @@ -17,6 +18,7 @@ export default function Modal() { {view === 'SessionSignTypedDataModal' && } {view === 'SessionSendTransactionModal' && } {view === 'SessionUnsuportedMethodModal' && } + {view === 'SessionSignCosmosModal' && } ) } diff --git a/wallets/react-wallet-v2/src/data/COSMOSData.ts b/wallets/react-wallet-v2/src/data/COSMOSData.ts index ae0d4aa..9d7bd87 100644 --- a/wallets/react-wallet-v2/src/data/COSMOSData.ts +++ b/wallets/react-wallet-v2/src/data/COSMOSData.ts @@ -15,3 +15,11 @@ export const COSMOS_MAINNET_CHAINS = { rpc: '' } } + +/** + * Methods + */ +export const COSMOS_SIGNING_METHODS = { + COSMOS_SIGN_DIRECT: 'cosmos_signDirect', + COSMOS_SIGN_AMINO: 'cosmos_signAmino' +} diff --git a/wallets/react-wallet-v2/src/hooks/useWalletConnectEventsManager.ts b/wallets/react-wallet-v2/src/hooks/useWalletConnectEventsManager.ts index 8c4358e..0d31254 100644 --- a/wallets/react-wallet-v2/src/hooks/useWalletConnectEventsManager.ts +++ b/wallets/react-wallet-v2/src/hooks/useWalletConnectEventsManager.ts @@ -1,3 +1,4 @@ +import { COSMOS_SIGNING_METHODS } from '@/data/COSMOSData' import { EIP155_SIGNING_METHODS } from '@/data/EIP155Data' import ModalStore from '@/store/ModalStore' import { walletConnectClient } from '@/utils/WalletConnectUtil' @@ -40,6 +41,10 @@ export default function useWalletConnectEventsManager(initialized: boolean) { case EIP155_SIGNING_METHODS.ETH_SIGN_TRANSACTION: return ModalStore.open('SessionSendTransactionModal', { requestEvent, requestSession }) + case COSMOS_SIGNING_METHODS.COSMOS_SIGN_DIRECT: + case COSMOS_SIGNING_METHODS.COSMOS_SIGN_AMINO: + return ModalStore.open('SessionSignCosmosModal', { requestEvent, requestSession }) + default: return ModalStore.open('SessionUnsuportedMethodModal', { requestEvent, requestSession }) } diff --git a/wallets/react-wallet-v2/src/store/ModalStore.ts b/wallets/react-wallet-v2/src/store/ModalStore.ts index 656d1ba..1225f19 100644 --- a/wallets/react-wallet-v2/src/store/ModalStore.ts +++ b/wallets/react-wallet-v2/src/store/ModalStore.ts @@ -19,6 +19,7 @@ interface State { | 'SessionSignTypedDataModal' | 'SessionSendTransactionModal' | 'SessionUnsuportedMethodModal' + | 'SessionSignCosmosModal' data?: ModalData } diff --git a/wallets/react-wallet-v2/src/utils/CosmosRequestHandler.ts b/wallets/react-wallet-v2/src/utils/CosmosRequestHandler.ts new file mode 100644 index 0000000..a15c496 --- /dev/null +++ b/wallets/react-wallet-v2/src/utils/CosmosRequestHandler.ts @@ -0,0 +1,30 @@ +import { COSMOS_SIGNING_METHODS } from '@/data/COSMOSData' +import { cosmosAddresses, cosmosWallets } from '@/utils/CosmosWalletUtil' +import { getWalletAddressFromParams } from '@/utils/HelperUtil' +import { formatJsonRpcError, formatJsonRpcResult } from '@json-rpc-tools/utils' +import { RequestEvent } from '@walletconnect/types' +import { ERROR } from '@walletconnect/utils' + +export async function approveCosmosRequest(requestEvent: RequestEvent) { + const { method, params, id } = requestEvent.request + const wallet = cosmosWallets[getWalletAddressFromParams(cosmosAddresses, params)] + + switch (method) { + case COSMOS_SIGNING_METHODS.COSMOS_SIGN_DIRECT: + const signedDirect = await wallet.signDirect(params.signerAddress, params.signDoc) + return formatJsonRpcResult(id, signedDirect.signature) + + case COSMOS_SIGNING_METHODS.COSMOS_SIGN_AMINO: + const signedAmino = await wallet.signAmino(params.signerAddress, params.signDoc) + return formatJsonRpcResult(id, signedAmino.signature) + + default: + throw new Error(ERROR.UNKNOWN_JSONRPC_METHOD.format().message) + } +} + +export function rejectCosmosRequest(request: RequestEvent['request']) { + const { id } = request + + return formatJsonRpcError(id, ERROR.JSONRPC_REQUEST_METHOD_REJECTED.format().message) +} diff --git a/wallets/react-wallet-v2/src/utils/CosmosUtil.ts b/wallets/react-wallet-v2/src/utils/CosmosUtil.ts index 4a4ce0c..912b6f0 100644 --- a/wallets/react-wallet-v2/src/utils/CosmosUtil.ts +++ b/wallets/react-wallet-v2/src/utils/CosmosUtil.ts @@ -1,3 +1,5 @@ +// @ts-expect-error +import { SignDoc } from '@cosmjs/proto-signing/build/codec/cosmos/tx/v1beta1/tx' import CosmosWallet from 'cosmos-wallet' import MnemonicKeyring from 'mnemonic-keyring' @@ -42,4 +44,12 @@ export class Cosmos { public getMnemonic() { return this.keyring.mnemonic } + + public signDirect(address: string, signDoc: SignDoc) { + return this.wallet.signDirect(address, signDoc) + } + + public signAmino(address: string, signDoc: SignDoc) { + return this.wallet.signAmino(address, signDoc) + } } diff --git a/wallets/react-wallet-v2/src/utils/HelperUtil.ts b/wallets/react-wallet-v2/src/utils/HelperUtil.ts index 8c7b3a1..64a4bcf 100644 --- a/wallets/react-wallet-v2/src/utils/HelperUtil.ts +++ b/wallets/react-wallet-v2/src/utils/HelperUtil.ts @@ -70,10 +70,16 @@ export function getWalletAddressFromParams(addresses: string[], params: any) { return address } +/** + * Check if chain is part of EIP155 standard + */ export function isEIP155Chain(chain: string) { return chain.includes('eip155') } +/** + * Check if chain is part of COSMOS standard + */ export function isCosmosChain(chain: string) { return chain.includes('cosmos') } diff --git a/wallets/react-wallet-v2/src/views/SessionSignCosmosModal.tsx b/wallets/react-wallet-v2/src/views/SessionSignCosmosModal.tsx new file mode 100644 index 0000000..412e228 --- /dev/null +++ b/wallets/react-wallet-v2/src/views/SessionSignCosmosModal.tsx @@ -0,0 +1,125 @@ +import { COSMOS_MAINNET_CHAINS, TCosmosChain } from '@/data/COSMOSData' +import ModalStore from '@/store/ModalStore' +import { approveCosmosRequest, rejectCosmosRequest } from '@/utils/CosmosRequestHandler' +import { walletConnectClient } from '@/utils/WalletConnectUtil' +import { Avatar, Button, Col, Container, Divider, Link, Modal, Row, Text } from '@nextui-org/react' +import { Fragment } from 'react' + +export default function SessionSignCosmosModal() { + // Get request and wallet data from store + const requestEvent = ModalStore.state.data?.requestEvent + const requestSession = ModalStore.state.data?.requestSession + + // Ensure request and wallet are defined + if (!requestEvent || !requestSession) { + return Missing request data + } + + // Get required request data + const { chainId } = requestEvent + const { method, params } = requestEvent.request + const { protocol } = requestSession.relay + const { name, icons, url } = requestSession.peer.metadata + + // Handle approve action (logic varies based on request method) + async function onApprove() { + if (requestEvent) { + const response = await approveCosmosRequest(requestEvent) + await walletConnectClient.respond({ + topic: requestEvent.topic, + response + }) + ModalStore.close() + } + } + + // Handle reject action + async function onReject() { + if (requestEvent) { + const response = rejectCosmosRequest(requestEvent.request) + await walletConnectClient.respond({ + topic: requestEvent.topic, + response + }) + ModalStore.close() + } + } + + return ( + + + Sign Message + + + + + + + + + + {name} + {url} + + + + + + + + Blockchain + + {COSMOS_MAINNET_CHAINS[chainId as TCosmosChain]?.name ?? chainId} + + + + + + + + + Message + {JSON.stringify(params.signDoc.msgs) ?? 'No Message'} + + + + + + + + Memo + {JSON.stringify(params.signDoc.memo)} + + + + + + + + Method + {method} + + + + + + + + Relay Protocol + {protocol} + + + + + + + + + + + ) +} diff --git a/wallets/react-wallet-v2/yarn.lock b/wallets/react-wallet-v2/yarn.lock index 645bf4f..8f5c634 100644 --- a/wallets/react-wallet-v2/yarn.lock +++ b/wallets/react-wallet-v2/yarn.lock @@ -143,6 +143,16 @@ "@babel/helper-validator-identifier" "^7.16.7" to-fast-properties "^2.0.0" +"@cosmjs/amino@0.27.1": + version "0.27.1" + resolved "https://registry.yarnpkg.com/@cosmjs/amino/-/amino-0.27.1.tgz#0910256b5aecd794420bb5f7319d98fc63252fa1" + integrity sha512-w56ar/nK9+qlvWDpBPRmD0Blk2wfkkLqRi1COs1x7Ll1LF0AtkIBUjbRKplENLbNovK0T3h+w8bHiFm+GBGQOA== + dependencies: + "@cosmjs/crypto" "0.27.1" + "@cosmjs/encoding" "0.27.1" + "@cosmjs/math" "0.27.1" + "@cosmjs/utils" "0.27.1" + "@cosmjs/amino@^0.25.4", "@cosmjs/amino@^0.25.6": version "0.25.6" resolved "https://registry.yarnpkg.com/@cosmjs/amino/-/amino-0.25.6.tgz#cdf9632253bfab7b1d2ef967124953d7bf16351f" @@ -153,6 +163,22 @@ "@cosmjs/math" "^0.25.6" "@cosmjs/utils" "^0.25.6" +"@cosmjs/crypto@0.27.1": + version "0.27.1" + resolved "https://registry.yarnpkg.com/@cosmjs/crypto/-/crypto-0.27.1.tgz#271c853089a3baf3acd6cf0b2122fd49f8815743" + integrity sha512-vbcxwSt99tIYJg8Spp00wc3zx72qx+pY3ozGuBN8gAvySnagK9dQ/jHwtWQWdammmdD6oW+75WfIHZ+gNa+Ybg== + dependencies: + "@cosmjs/encoding" "0.27.1" + "@cosmjs/math" "0.27.1" + "@cosmjs/utils" "0.27.1" + bip39 "^3.0.2" + bn.js "^5.2.0" + elliptic "^6.5.3" + js-sha3 "^0.8.0" + libsodium-wrappers "^0.7.6" + ripemd160 "^2.0.2" + sha.js "^2.4.11" + "@cosmjs/crypto@^0.25.6": version "0.25.6" resolved "https://registry.yarnpkg.com/@cosmjs/crypto/-/crypto-0.25.6.tgz#695d2d0d2195bdbdd5825d415385646244900bbb" @@ -169,6 +195,15 @@ ripemd160 "^2.0.2" sha.js "^2.4.11" +"@cosmjs/encoding@0.27.1": + version "0.27.1" + resolved "https://registry.yarnpkg.com/@cosmjs/encoding/-/encoding-0.27.1.tgz#3cd5bc0af743485eb2578cdb08cfa84c86d610e1" + integrity sha512-rayLsA0ojHeniaRfWWcqSsrE/T1rl1gl0OXVNtXlPwLJifKBeLEefGbOUiAQaT0wgJ8VNGBazVtAZBpJidfDhw== + dependencies: + base64-js "^1.3.0" + bech32 "^1.1.4" + readonly-date "^1.0.0" + "@cosmjs/encoding@^0.25.6": version "0.25.6" resolved "https://registry.yarnpkg.com/@cosmjs/encoding/-/encoding-0.25.6.tgz#da741a33eaf063a6d3611d7d68db5ca3938e0ef5" @@ -178,6 +213,13 @@ bech32 "^1.1.4" readonly-date "^1.0.0" +"@cosmjs/math@0.27.1": + version "0.27.1" + resolved "https://registry.yarnpkg.com/@cosmjs/math/-/math-0.27.1.tgz#be78857b008ffc6b1ed6fecaa1c4cd5bc38c07d7" + integrity sha512-cHWVjmfIjtRc7f80n7x+J5k8pe+vTVTQ0lA82tIxUgqUvgS6rogPP/TmGtTiZ4+NxWxd11DUISY6gVpr18/VNQ== + dependencies: + bn.js "^5.2.0" + "@cosmjs/math@^0.25.6": version "0.25.6" resolved "https://registry.yarnpkg.com/@cosmjs/math/-/math-0.25.6.tgz#25c7b106aaded889a5b80784693caa9e654b0c28" @@ -185,6 +227,18 @@ dependencies: bn.js "^4.11.8" +"@cosmjs/proto-signing@0.27.1": + version "0.27.1" + resolved "https://registry.yarnpkg.com/@cosmjs/proto-signing/-/proto-signing-0.27.1.tgz#1f8f662550aab012d957d02f43c77d914c2ae0db" + integrity sha512-t7/VvQivMdM1KgKWai/9ZCEcGFXJtr9Xo0hGcPLTn9wGkh9tmOsUXINYVMsf5D/jWIm1MDPbGCYfdb9V1Od4hw== + dependencies: + "@cosmjs/amino" "0.27.1" + "@cosmjs/crypto" "0.27.1" + "@cosmjs/math" "0.27.1" + cosmjs-types "^0.4.0" + long "^4.0.0" + protobufjs "~6.10.2" + "@cosmjs/proto-signing@^0.25.4": version "0.25.6" resolved "https://registry.yarnpkg.com/@cosmjs/proto-signing/-/proto-signing-0.25.6.tgz#d9fc57b8e0a46cda97e192bd0435157b24949ff8" @@ -194,6 +248,11 @@ long "^4.0.0" protobufjs "~6.10.2" +"@cosmjs/utils@0.27.1": + version "0.27.1" + resolved "https://registry.yarnpkg.com/@cosmjs/utils/-/utils-0.27.1.tgz#1c8efde17256346ef142a3bd15158ee4055470e2" + integrity sha512-VG7QPDiMUzVPxRdJahDV8PXxVdnuAHiIuG56hldV4yPnOz/si/DLNd7VAUUA5923b6jS1Hhev0Hr6AhEkcxBMg== + "@cosmjs/utils@^0.25.6": version "0.25.6" resolved "https://registry.yarnpkg.com/@cosmjs/utils/-/utils-0.25.6.tgz#934d9a967180baa66163847616a74358732227ca" @@ -938,7 +997,7 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-11.11.6.tgz#df929d1bb2eee5afdda598a41930fe50b43eaa6a" integrity sha512-Exw4yUWMBXM3X+8oqzJNRqZSwUAaS4+7NdvHqQuFi/d+synz++xmX3QIf+BFqneW8N31R8Ky+sikfZUXq07ggQ== -"@types/node@17.0.21": +"@types/node@17.0.21", "@types/node@>=13.7.0": version "17.0.21" resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.21.tgz#864b987c0c68d07b4345845c3e63b75edd143644" integrity sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ== @@ -1427,6 +1486,11 @@ bn.js@^4.11.8, bn.js@^4.11.9: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== +bn.js@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" + integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -1617,6 +1681,14 @@ core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== +cosmjs-types@^0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/cosmjs-types/-/cosmjs-types-0.4.1.tgz#3b2a53ba60d33159dd075596ce8267cfa7027063" + integrity sha512-I7E/cHkIgoJzMNQdFF0YVqPlaTqrqKHrskuSTIqlEyxfB5Lf3WKCajSXVK2yHOfOFfSux/RxEdpMzw/eO4DIog== + dependencies: + long "^4.0.0" + protobufjs "~6.11.2" + cosmos-wallet@1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/cosmos-wallet/-/cosmos-wallet-1.1.0.tgz#ca554d19fda1a1a7a8ee21fef4e0ef8f7fa9ef7b" @@ -3285,6 +3357,25 @@ protobufjs@~6.10.2: "@types/node" "^13.7.0" long "^4.0.0" +protobufjs@~6.11.2: + version "6.11.2" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.2.tgz#de39fabd4ed32beaa08e9bb1e30d08544c1edf8b" + integrity sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw== + dependencies: + "@protobufjs/aspromise" "^1.1.2" + "@protobufjs/base64" "^1.1.2" + "@protobufjs/codegen" "^2.0.4" + "@protobufjs/eventemitter" "^1.1.0" + "@protobufjs/fetch" "^1.1.0" + "@protobufjs/float" "^1.0.2" + "@protobufjs/inquire" "^1.1.0" + "@protobufjs/path" "^1.1.2" + "@protobufjs/pool" "^1.1.0" + "@protobufjs/utf8" "^1.1.0" + "@types/long" "^4.0.1" + "@types/node" ">=13.7.0" + long "^4.0.0" + proxy-compare@2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/proxy-compare/-/proxy-compare-2.0.3.tgz#13fac7a72ca186a3f4a5e99bdff4863f2ced85c1" @@ -3898,10 +3989,10 @@ typeforce@^1.11.5: resolved "https://registry.yarnpkg.com/typeforce/-/typeforce-1.18.0.tgz#d7416a2c5845e085034d70fcc5b6cc4a90edbfdc" integrity sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g== -typescript@4.5.5: - version "4.5.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.5.tgz#d8c953832d28924a9e3d37c73d729c846c5896f3" - integrity sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA== +typescript@4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.2.tgz#fe12d2727b708f4eef40f51598b3398baa9611d4" + integrity sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg== unbox-primitive@^1.0.1: version "1.0.1"