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

wc-client: Secret Network support #728

Open
wants to merge 21 commits into
base: Thunnini/prepare-mobile-merge
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
526ea95
add secret network to wc-client-example
luca992 May 15, 2023
88d58f4
wc-client implement getEnigmaUtils, enigmaDecrypt, enigmaEncrypt,
luca992 May 16, 2023
229294a
wc-client implement getEnigmaPubKey, getEnigmaTxEncryptionKey
luca992 May 16, 2023
ce1da85
wc-client: implement suggestToken
luca992 May 16, 2023
fbf05a9
change secret endpoints to use keplr
luca992 May 16, 2023
8bcd6f9
implement getSecret20ViewingKey
luca992 May 16, 2023
382fe7d
implement getSecret20ViewingKeyOrPermit for keplr and wc-client
luca992 May 16, 2023
cb54b74
implement getSecret20ViewingKeyOrPermit everywhere
luca992 May 16, 2023
d9fa36f
check types
luca992 May 16, 2023
9b18890
encode Uint8Array arrays to base64
luca992 May 16, 2023
7c08418
add decrypt button example
luca992 May 16, 2023
e181edb
update console logs in example
luca992 May 16, 2023
4ac27b7
AddTokenModal: add support for secret
luca992 May 22, 2023
9084866
Merge remote-tracking branch 'orgin/Thunnini/prepare-mobile-merge' in…
luca992 May 22, 2023
dad69c2
AddTokenModal: add support for secret viewing keys
luca992 May 22, 2023
114f3bd
AddTokenModal: add support for creating a viewing key if non is set
luca992 May 22, 2023
1d9ebf6
Terminal AddTokenModal: select the specified chain. Not current chain
luca992 May 22, 2023
c82d847
wc-client-example: add juno and add cw20 suggestToken test
luca992 May 22, 2023
12fd9ae
add todo to getSecret20ViewingKeyOrPermit to implement permit support
luca992 May 30, 2023
f5bfa81
Merge remote-tracking branch 'orgin/Thunnini/prepare-mobile-merge' in…
luca992 Jun 27, 2023
bd6dc5a
rename getSecret20ViewingKeyOrPermit -> getSecret20QueryAuthorization
luca992 Jun 27, 2023
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
127 changes: 108 additions & 19 deletions packages/mobile/src/modals/add-token/index.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
import React, { FunctionComponent } from "react";
import React, { FunctionComponent, useState } from "react";
import { registerModal } from "../base";
import { CardModal } from "../card";
import { useStore } from "../../stores";
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();

Expand All @@ -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<string> => {
return new Promise((resolve, reject) => {
account.secret
.createSecret20ViewingKey(
contractAddress,
"",
{},
{},
(_, viewingKey) => {
resolve(viewingKey);
}
)
.catch(reject);
});
};

return (
<CardModal title="Add Token">
Expand All @@ -37,33 +71,88 @@ export const AddTokenModal: FunctionComponent<{
<TextInput
label="Name"
editable={false}
value={queryTokenInfo.tokenInfo?.name ?? ""}
value={tokenInfo?.name ?? ""}
/>
<TextInput
label="Symbol"
editable={false}
value={queryTokenInfo.tokenInfo?.symbol ?? ""}
value={tokenInfo?.symbol ?? ""}
/>
<TextInput
label="Decimals"
editable={false}
value={queryTokenInfo.tokenInfo?.decimals.toString() ?? ""}
value={tokenInfo?.decimals.toString() ?? ""}
/>
{isSecret20 ? (
<View
style={StyleSheet.flatten([
style.flatten(["flex-row", "justify-center"]),
])}
>
<Button
text="Advanced"
mode="text"
rightIcon={
<View style={style.flatten(["padding-left-4"])}>
{isAdvanced ? (
<UpArrowIcon size={16} color="#314FDF" />
) : (
<DownArrowIcon size={16} color="#314FDF" />
)}
</View>
}
style={StyleSheet.flatten([
style.flatten(["width-122", "items-center"]),
])}
onPress={() => {
setAdvanced(!isAdvanced);
}}
/>
</View>
) : null}
{isAdvanced ? (
<TextInput
label="Viewing key"
placeholder="Import my own viewing key"
value={viewingKey}
onChangeText={setViewingKey}
/>
) : null}
<View style={style.flatten(["height-16"])} />
<Button
text="Submit"
size="large"
disabled={!queryTokenInfo.tokenInfo || queryTokenInfo.error != null}
loading={!queryTokenInfo.tokenInfo && queryTokenInfo.isFetching}
disabled={!tokenInfo || queryContractInfo.error != null}
loading={!tokenInfo && queryContractInfo.isFetching}
onPress={async () => {
if (queryTokenInfo.tokenInfo) {
await tokensStore.approveSuggestedToken({
type: "cw20",
contractAddress,
coinMinimalDenom: queryTokenInfo.tokenInfo.name,
coinDenom: queryTokenInfo.tokenInfo.symbol,
coinDecimals: queryTokenInfo.tokenInfo.decimals,
});
if (tokenInfo) {
let currency: AppCurrency;

if (isSecret20) {
let newViewingKey = viewingKey;

if (!viewingKey) {
newViewingKey = await createViewingKey();
}

currency = {
type: "secret20",
contractAddress,
viewingKey: newViewingKey,
coinMinimalDenom: tokenInfo.name,
coinDenom: tokenInfo.symbol,
coinDecimals: tokenInfo.decimals,
};
} else {
currency = {
type: "cw20",
contractAddress,
coinMinimalDenom: tokenInfo.name,
coinDenom: tokenInfo.symbol,
coinDecimals: tokenInfo.decimals,
};
}
await tokensStore.approveSuggestedToken(currency);
}
}}
/>
Expand Down
122 changes: 122 additions & 0 deletions packages/mobile/src/stores/wallet-connect/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,128 @@ export abstract class WalletConnectManager {
});
break;
}
case "keplr_enigma_decrypt_wallet_connect_v1": {
if (payload.params.length !== 3) {
throw new Error("Invalid parmas");
}
for (const param of payload.params) {
if (typeof param !== "string") {
throw new Error("Invalid parmas");
}
}
const result = await keplr.enigmaDecrypt(
payload.params[0],
Buffer.from(payload.params[1], "base64"),
Buffer.from(payload.params[2], "base64")
);

client.approveRequest({
id,
result: [Buffer.from(result).toString("base64")],
});
break;
}
case "keplr_enigma_encrypt_wallet_connect_v1": {
if (payload.params.length !== 3) {
throw new Error("Invalid parmas");
}
if (
typeof payload.params[0] !== "string" &&
typeof payload.params[1] !== "string" &&
typeof payload.params[2] !== "object"
) {
throw new Error("Invalid parmas");
}
const result = await keplr.enigmaEncrypt(
payload.params[0],
payload.params[1],
payload.params[2]
);

client.approveRequest({
id,
result: [Buffer.from(result).toString("base64")],
});
break;
}
case "keplr_enigma_pub_key_wallet_connect_v1": {
if (payload.params.length !== 1) {
throw new Error("Invalid parmas");
}
if (typeof payload.params[0] !== "string") {
throw new Error("Invalid parmas");
}
const result = await keplr.getEnigmaPubKey(payload.params[0]);

client.approveRequest({
id,
result: [Buffer.from(result).toString("base64")],
});
break;
}
case "keplr_enigma_tx_encryption_key_wallet_connect_v1": {
if (payload.params.length !== 2) {
throw new Error("Invalid parmas");
}
for (const param of payload.params) {
if (typeof param !== "string") {
throw new Error("Invalid parmas");
}
}
const result = await keplr.getEnigmaTxEncryptionKey(
payload.params[0],
Buffer.from(payload.params[1], "base64")
);

client.approveRequest({
id,
result: [Buffer.from(result).toString("base64")],
});
break;
}
case "keplr_suggest_token_wallet_connect_v1": {
if (payload.params.length !== 2 && payload.params.length !== 3) {
throw new Error("Invalid parmas");
}
if (
typeof payload.params[0] !== "string" &&
typeof payload.params[1] !== "string" &&
(typeof payload.params[2] !== "string" ||
typeof payload.params[2] !== undefined)
) {
throw new Error("Invalid parmas");
}
await keplr.suggestToken(
payload.params[0],
payload.params[1],
payload.params[2]
);

client.approveRequest({
id,
result: [],
});
break;
}
case "keplr_get_scrt20_viewing_key_wallet_connect_v1": {
if (payload.params.length !== 2) {
throw new Error("Invalid parmas");
}
for (const param of payload.params) {
if (typeof param !== "string") {
throw new Error("Invalid parmas");
}
}
const result = await keplr.getSecret20ViewingKey(
payload.params[0],
payload.params[1]
);
client.approveRequest({
id,
result: [result],
});
break;
}
default:
throw new Error(`Unknown method (${payload.method})`);
}
Expand Down
7 changes: 7 additions & 0 deletions packages/provider-mock/src/mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,13 @@ export class MockKeplr implements Keplr {
throw new Error("Not implemented");
}

getSecret20QueryAuthorization(
_chainId: string,
_contractAddress: string
): Promise<{ permit: any | undefined; viewing_key: string | undefined }> {
throw new Error("Not implemented");
}

sendTx(
chainId: string,
stdTx: Uint8Array,
Expand Down
11 changes: 11 additions & 0 deletions packages/provider/src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,17 @@ export class Keplr implements IKeplr, KeplrCoreTypes {
return await this.requester.sendMessage(BACKGROUND_PORT, msg);
}

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),
};
}

async getEnigmaPubKey(chainId: string): Promise<Uint8Array> {
return await this.requester.sendMessage(
BACKGROUND_PORT,
Expand Down
11 changes: 11 additions & 0 deletions packages/provider/src/inject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,17 @@ export class InjectedKeplr implements IKeplr {
]);
}

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),
};
}

async getEnigmaPubKey(chainId: string): Promise<Uint8Array> {
return await this.requestMethod("getEnigmaPubKey", [chainId]);
}
Expand Down
4 changes: 4 additions & 0 deletions packages/types/src/wallet/keplr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@ export interface Keplr {
chainId: string,
contractAddress: string
): Promise<string>;
getSecret20QueryAuthorization(
chainId: string,
contractAddress: string
): Promise<{ permit: any | undefined; viewing_key: string | undefined }>;
getEnigmaUtils(chainId: string): SecretUtils;

// Related to Enigma.
Expand Down
Loading