diff --git a/packages/mobile/src/modals/add-token/index.tsx b/packages/mobile/src/modals/add-token/index.tsx index a4de26fcee..c3aeee7154 100644 --- a/packages/mobile/src/modals/add-token/index.tsx +++ b/packages/mobile/src/modals/add-token/index.tsx @@ -1,4 +1,4 @@ -import React, { FunctionComponent } from "react"; +import React, { FunctionComponent, useState } from "react"; import { registerModal } from "../base"; import { CardModal } from "../card"; import { useStore } from "../../stores"; @@ -6,14 +6,21 @@ import { observer } from "mobx-react-lite"; import { TextInput } from "../../components/input"; import { Button } from "../../components/button"; import { useStyle } from "../../styles"; -import { View } from "react-native"; +import { StyleSheet, View } from "react-native"; +import { DownArrowIcon, UpArrowIcon } from "../../components/icon"; +import { AppCurrency } from "@keplr-wallet/types"; export const AddTokenModal: FunctionComponent<{ isOpen: boolean; close: () => void; }> = registerModal( observer(() => { - const { chainStore, queriesStore, tokensStore } = useStore(); + const { chainStore, accountStore, queriesStore, tokensStore } = useStore(); + + const [isAdvanced, setAdvanced] = useState(false); + const [viewingKey, setViewingKey] = useState( + tokensStore.waitingSuggestedToken?.data?.viewingKey ?? "" + ); const style = useStyle(); @@ -22,10 +29,37 @@ export const AddTokenModal: FunctionComponent<{ chainStore.current.chainId; const contractAddress = tokensStore.waitingSuggestedToken?.data.contractAddress || ""; + const account = accountStore.getAccount(chainId); + + const isSecret20 = + (chainStore.getChain(chainId).features ?? []).find( + (feature) => feature === "secretwasm" + ) != null; + + const queries = queriesStore.get(chainId); + + const query = isSecret20 + ? queries.secret.querySecret20ContractInfo + : queries.cosmwasm.querycw20ContractInfo; - const queryTokenInfo = queriesStore - .get(chainId) - .cosmwasm.querycw20ContractInfo.getQueryContract(contractAddress); + const queryContractInfo = query.getQueryContract(contractAddress); + const tokenInfo = queryContractInfo.tokenInfo; + + const createViewingKey = async (): Promise => { + return new Promise((resolve, reject) => { + account.secret + .createSecret20ViewingKey( + contractAddress, + "", + {}, + {}, + (_, viewingKey) => { + resolve(viewingKey); + } + ) + .catch(reject); + }); + }; return ( @@ -37,33 +71,88 @@ export const AddTokenModal: FunctionComponent<{ + {isSecret20 ? ( + + + + + + + + + + + ); }); diff --git a/packages/wc-client-example/src/config.ts b/packages/wc-client-example/src/config.ts index b1006d0e5e..18f844f908 100644 --- a/packages/wc-client-example/src/config.ts +++ b/packages/wc-client-example/src/config.ts @@ -93,4 +93,90 @@ export const EmbedChainInfos = [ }, features: ["stargate", "ibc-transfer"], }, + { + chainId: "secret-4", + chainName: "Secret", + rpc: "https://rpc-secret.keplr.app", + rest: "https://lcd-secret.keplr.app", + bip44: { coinType: 529 }, + coinType: 529, + stakeCurrency: { + coinDenom: "$denom", + coinMinimalDenom: "$minimalDenom", + coinDecimals: 6, + }, + bech32Config: { + bech32PrefixAccAddr: "secret", + bech32PrefixAccPub: "secretpub", + bech32PrefixValAddr: "secretvaloper", + bech32PrefixValPub: "secretvaloperpub", + bech32PrefixConsAddr: "secretvalcons", + bech32PrefixConsPub: "secretvalconspub", + }, + currencies: [ + { + coinDenom: "$denom", + coinMinimalDenom: "$minimalDenom", + coinDecimals: 6, + }, + ], + feeCurrencies: [ + { + coinDenom: "$denom", + coinMinimalDenom: "$minimalDenom", + coinDecimals: 6, + gasPriceStep: { + low: 0.1, + average: 0.25, + high: 0.4, + }, + }, + ], + features: ["secretwasm"], + }, + { + chainId: "juno-1", + chainName: "Juno", + rpc: "https://rpc-juno.mib.tech:443/", + rest: "https://lcd-juno.itastakers.com:443/", + stakeCurrency: { + coinDenom: "JUNO", + coinMinimalDenom: "ujuno", + coinDecimals: 6, + }, + bip44: { + coinType: 118, + }, + bech32Config: { + bech32PrefixAccAddr: "juno", + bech32PrefixAccPub: "junopub", + bech32PrefixValAddr: "junovaloper", + bech32PrefixValPub: "junovaloperpub", + bech32PrefixConsAddr: "junovalcons", + bech32PrefixConsPub: "junovalconspub", + }, + currencies: [ + { + coinDenom: "JUNO", + coinMinimalDenom: "ujuno", + coinDecimals: 6, + coinGeckoId: "juno-network", + }, + ], + feeCurrencies: [ + { + coinDenom: "JUNO", + coinMinimalDenom: "ujuno", + coinDecimals: 6, + coinGeckoId: "juno-network", + }, + ], + coinType: 118, + gasPriceStep: { + low: 0.0025, + average: 0.01, + high: 0.025, + }, + features: ["cosmwasm", "stargate", "ibc-transfer"], + }, ]; diff --git a/packages/wc-client/src/index.ts b/packages/wc-client/src/index.ts index b183f271d0..2737c44b88 100644 --- a/packages/wc-client/src/index.ts +++ b/packages/wc-client/src/index.ts @@ -22,6 +22,7 @@ import { import { CosmJSOfflineSigner, CosmJSOfflineSignerOnlyAmino, + KeplrEnigmaUtils, } from "@keplr-wallet/provider"; import { SecretUtils } from "secretjs/types/enigmautils"; import { payloadId } from "@walletconnect/utils"; @@ -72,6 +73,8 @@ export type KeplrKeystoreMayChangedEventParam = { }; export class KeplrWalletConnectV1 implements Keplr { + protected enigmaUtils: Map = new Map(); + constructor( public readonly connector: IConnector, public readonly options: { @@ -255,40 +258,83 @@ export class KeplrWalletConnectV1 implements Keplr { ); } - enigmaDecrypt( - _chainId: string, - _ciphertext: Uint8Array, - _nonce: Uint8Array + async enigmaDecrypt( + chainId: string, + ciphertext: Uint8Array, + nonce: Uint8Array ): Promise { - throw new Error("Not yet implemented"); + const encryptedBase64: string = ( + await this.sendCustomRequest({ + id: payloadId(), + jsonrpc: "2.0", + method: "keplr_enigma_decrypt_wallet_connect_v1", + params: [ + chainId, + Buffer.from(ciphertext).toString("base64"), + Buffer.from(nonce).toString("base64"), + ], + }) + )[0]; + return Buffer.from(encryptedBase64, "base64"); } - enigmaEncrypt( - _chainId: string, - _contractCodeHash: string, + async enigmaEncrypt( + chainId: string, + contractCodeHash: string, // eslint-disable-next-line @typescript-eslint/ban-types - _msg: object + msg: object ): Promise { - throw new Error("Not yet implemented"); + const encryptedBase64: string = ( + await this.sendCustomRequest({ + id: payloadId(), + jsonrpc: "2.0", + method: "keplr_enigma_encrypt_wallet_connect_v1", + params: [chainId, contractCodeHash, msg], + }) + )[0]; + return Buffer.from(encryptedBase64, "base64"); } experimentalSuggestChain(_chainInfo: ChainInfo): Promise { throw new Error("Not yet implemented"); } - getEnigmaPubKey(_chainId: string): Promise { - throw new Error("Not yet implemented"); + async getEnigmaPubKey(chainId: string): Promise { + const encryptedBase64: string = ( + await this.sendCustomRequest({ + id: payloadId(), + jsonrpc: "2.0", + method: "keplr_enigma_pub_key_wallet_connect_v1", + params: [chainId], + }) + )[0]; + return Buffer.from(encryptedBase64, "base64"); } - getEnigmaTxEncryptionKey( - _chainId: string, - _nonce: Uint8Array + async getEnigmaTxEncryptionKey( + chainId: string, + nonce: Uint8Array ): Promise { - throw new Error("Not yet implemented"); + const encryptedBase64: string = ( + await this.sendCustomRequest({ + id: payloadId(), + jsonrpc: "2.0", + method: "keplr_enigma_tx_encryption_key_wallet_connect_v1", + params: [chainId, Buffer.from(nonce).toString("base64")], + }) + )[0]; + return Buffer.from(encryptedBase64, "base64"); } - getEnigmaUtils(_chainId: string): SecretUtils { - throw new Error("Not yet implemented"); + getEnigmaUtils(chainId: string): SecretUtils { + if (this.enigmaUtils.has(chainId)) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + return this.enigmaUtils.get(chainId)!; + } + + const enigmaUtils = new KeplrEnigmaUtils(chainId, this); + this.enigmaUtils.set(chainId, enigmaUtils); + return enigmaUtils; } async getKey(chainId: string): Promise { @@ -412,11 +458,29 @@ export class KeplrWalletConnectV1 implements Keplr { return new CosmJSOfflineSignerOnlyAmino(chainId, this); } - getSecret20ViewingKey( - _chainId: string, - _contractAddress: string + async getSecret20ViewingKey( + chainId: string, + contractAddress: string ): Promise { - throw new Error("Not yet implemented"); + return ( + await this.sendCustomRequest({ + id: payloadId(), + jsonrpc: "2.0", + method: "keplr_get_scrt20_viewing_key_wallet_connect_v1", + params: [chainId, contractAddress], + }) + )[0]; + } + + async getSecret20QueryAuthorization( + chainId: string, + contractAddress: string + ): Promise<{ permit: any | undefined; viewing_key: string | undefined }> { + return { + // todo: once support for saving permits is implemented, getting a saved permit should be implemented here + permit: null, + viewing_key: await this.getSecret20ViewingKey(chainId, contractAddress), + }; } /** @@ -475,12 +539,17 @@ export class KeplrWalletConnectV1 implements Keplr { throw new Error("Not yet implemented"); } - suggestToken( - _chainId: string, - _contractAddress: string, - _viewingKey?: string + async suggestToken( + chainId: string, + contractAddress: string, + viewingKey?: string ): Promise { - throw new Error("Not yet implemented"); + await this.sendCustomRequest({ + id: payloadId(), + jsonrpc: "2.0", + method: "keplr_suggest_token_wallet_connect_v1", + params: [chainId, contractAddress, viewingKey], + }); } experimentalSignEIP712CosmosTx_v0(