From 318c0dbcb0394208d7bbe9386f1b45fd3e0700ad Mon Sep 17 00:00:00 2001 From: Ilya Raykker Date: Fri, 4 Oct 2024 16:00:20 +0200 Subject: [PATCH] Add support for SecretNetwork v1.15 --- .../src/secret-wasm/enigma-utils.ts | 47 ++++- .../background/src/secret-wasm/handler.ts | 21 ++ packages/background/src/secret-wasm/init.ts | 2 + .../background/src/secret-wasm/messages.ts | 28 +++ .../background/src/secret-wasm/service.ts | 13 ++ packages/proto-types/outputHash | 2 +- .../proto/secret/compute/v1/genesis.proto | 45 +++++ .../proto/secret/compute/v1/msg.proto | 189 ++++++++++++++++++ .../proto/secret/compute/v1/query.proto | 172 ++++++++++++++++ .../proto/secret/compute/v1/types.proto | 110 ++++++++++ .../secret/registration/v1/genesis.proto | 17 ++ .../proto/secret/registration/v1/msg.proto | 45 +++++ .../proto/secret/registration/v1/query.proto | 37 ++++ .../v1/remote_attestation/types.proto | 58 ++++++ .../proto/secret/registration/v1/types.proto | 26 +++ .../proto-types-gen/scripts/proto-gen.mjs | 1 + packages/provider-extension/src/enigma.ts | 4 + packages/provider-extension/src/keplr.ts | 4 + packages/provider-mock/src/mock.ts | 4 + packages/provider/src/core.ts | 12 ++ packages/provider/src/enigma.ts | 4 + packages/provider/src/inject.ts | 4 + packages/stores/src/account/secret.ts | 181 ++++++++++++----- packages/types/src/secretjs.ts | 1 + packages/types/src/wallet/keplr.ts | 1 + packages/wc-client/src/index.ts | 4 + 26 files changed, 971 insertions(+), 61 deletions(-) create mode 100644 packages/proto-types/proto-types-gen/proto/secret/compute/v1/genesis.proto create mode 100644 packages/proto-types/proto-types-gen/proto/secret/compute/v1/msg.proto create mode 100644 packages/proto-types/proto-types-gen/proto/secret/compute/v1/query.proto create mode 100644 packages/proto-types/proto-types-gen/proto/secret/compute/v1/types.proto create mode 100644 packages/proto-types/proto-types-gen/proto/secret/registration/v1/genesis.proto create mode 100644 packages/proto-types/proto-types-gen/proto/secret/registration/v1/msg.proto create mode 100644 packages/proto-types/proto-types-gen/proto/secret/registration/v1/query.proto create mode 100644 packages/proto-types/proto-types-gen/proto/secret/registration/v1/remote_attestation/types.proto create mode 100644 packages/proto-types/proto-types-gen/proto/secret/registration/v1/types.proto diff --git a/packages/background/src/secret-wasm/enigma-utils.ts b/packages/background/src/secret-wasm/enigma-utils.ts index 975ddba80d..fc1ccd6130 100644 --- a/packages/background/src/secret-wasm/enigma-utils.ts +++ b/packages/background/src/secret-wasm/enigma-utils.ts @@ -2,13 +2,18 @@ import { generateKeyPair, sharedKey as x25519 } from "curve25519-js"; import { hkdf } from "@noble/hashes/hkdf"; import { sha256 } from "@noble/hashes/sha256"; import * as miscreant from "miscreant"; -import { simpleFetch } from "@keplr-wallet/simple-fetch"; +import { + simpleFetch, + isSimpleFetchError, + SimpleFetchError, +} from "@keplr-wallet/simple-fetch"; import { Buffer } from "buffer/"; import { ChainIdHelper } from "@keplr-wallet/cosmos"; const cryptoProvider = new miscreant.PolyfillCryptoProvider(); export interface EncryptionUtils { + isNewApi: boolean; getPubkey: () => Promise; decrypt: (ciphertext: Uint8Array, nonce: Uint8Array) => Promise; encrypt: (contractCodeHash: string, msg: object) => Promise; @@ -29,6 +34,7 @@ const secretConsensusIoPubKey = new Uint8Array( export class EnigmaUtils implements EncryptionUtils { private readonly privkey: Uint8Array; public readonly pubkey: Uint8Array; + public isNewApi: boolean; private consensusIoPubKey: Uint8Array = new Uint8Array(); // cache public constructor( @@ -49,6 +55,7 @@ export class EnigmaUtils implements EncryptionUtils { if (ChainIdHelper.parse(chainId).identifier === "secret") { this.consensusIoPubKey = secretConsensusIoPubKey; } + this.isNewApi = false; } private static secureRandom(count: number): Uint8Array { @@ -71,14 +78,36 @@ export class EnigmaUtils implements EncryptionUtils { return this.consensusIoPubKey; } - const response = await simpleFetch<{ result: { TxKey: string } }>( - this.url, - "/reg/tx-key" - ); - - this.consensusIoPubKey = new Uint8Array( - Buffer.from(response.data.result.TxKey, "base64") - ); + // try to fetch from the old API + try { + const response = await simpleFetch<{ result: { TxKey: string } }>( + this.url, + "/reg/tx-key" + ); + + this.consensusIoPubKey = new Uint8Array( + Buffer.from(response.data.result.TxKey, "base64") + ); + } catch (e: any) { + if ( + !isSimpleFetchError(e) || + !(e as SimpleFetchError).response || + (e as SimpleFetchError).response?.status != 501 + ) { + throw e; + } + + // if old API is not implemented, use the new one + const response = await simpleFetch<{ key: string }>( + this.url, + "/registration/v1beta1/tx-key" + ); + + this.consensusIoPubKey = new Uint8Array( + Buffer.from(response.data.key, "base64") + ); + this.isNewApi = true; + } return this.consensusIoPubKey; } diff --git a/packages/background/src/secret-wasm/handler.ts b/packages/background/src/secret-wasm/handler.ts index 23f752ec0b..98f8ef72e9 100644 --- a/packages/background/src/secret-wasm/handler.ts +++ b/packages/background/src/secret-wasm/handler.ts @@ -10,6 +10,7 @@ import { GetTxEncryptionKeyMsg, ReqeustEncryptMsg, RequestDecryptMsg, + IsNewApiMsg, } from "./messages"; import { SecretWasmService } from "./service"; import { PermissionInteractiveService } from "../permission-interactive"; @@ -40,6 +41,11 @@ export const getHandler: ( service, permissionInteractionService )(env, msg as GetTxEncryptionKeyMsg); + case IsNewApiMsg: + return handleIsNewApiMsg(service, permissionInteractionService)( + env, + msg as IsNewApiMsg + ); default: throw new KeplrError("secret-wasm", 120, "Unknown msg type"); } @@ -64,6 +70,21 @@ const handleGetPubkeyMsg: ( }; }; +const handleIsNewApiMsg: ( + service: SecretWasmService, + permissionInteractionService: PermissionInteractiveService +) => InternalHandler = (service, permissionInteractionService) => { + return async (env, msg) => { + await permissionInteractionService.ensureEnabled( + env, + [msg.chainId], + msg.origin + ); + + return await service.isNewApi(msg.chainId); + }; +}; + const handleReqeustEncryptMsg: ( service: SecretWasmService, permissionInteractionService: PermissionInteractiveService diff --git a/packages/background/src/secret-wasm/init.ts b/packages/background/src/secret-wasm/init.ts index 7b8485a6fb..702cfafcdd 100644 --- a/packages/background/src/secret-wasm/init.ts +++ b/packages/background/src/secret-wasm/init.ts @@ -4,6 +4,7 @@ import { GetTxEncryptionKeyMsg, ReqeustEncryptMsg, RequestDecryptMsg, + IsNewApiMsg, } from "./messages"; import { SecretWasmService } from "./service"; import { ROUTE } from "./constants"; @@ -19,6 +20,7 @@ export function init( router.registerMessage(ReqeustEncryptMsg); router.registerMessage(RequestDecryptMsg); router.registerMessage(GetTxEncryptionKeyMsg); + router.registerMessage(IsNewApiMsg); router.addHandler(ROUTE, getHandler(service, permissionInteractionService)); } diff --git a/packages/background/src/secret-wasm/messages.ts b/packages/background/src/secret-wasm/messages.ts index b4e036a09e..b3bab676b3 100644 --- a/packages/background/src/secret-wasm/messages.ts +++ b/packages/background/src/secret-wasm/messages.ts @@ -145,3 +145,31 @@ export class GetTxEncryptionKeyMsg extends Message { return GetTxEncryptionKeyMsg.type(); } } + +export class IsNewApiMsg extends Message { + public static type() { + return "is-new-api-msg"; + } + + constructor(public readonly chainId: string) { + super(); + } + + validateBasic(): void { + if (!this.chainId) { + throw new KeplrError("secret-wasm", 100, "chain id not set"); + } + } + + override approveExternal(): boolean { + return true; + } + + route(): string { + return ROUTE; + } + + type(): string { + return IsNewApiMsg.type(); + } +} diff --git a/packages/background/src/secret-wasm/service.ts b/packages/background/src/secret-wasm/service.ts index 51681a5404..3e0549c660 100644 --- a/packages/background/src/secret-wasm/service.ts +++ b/packages/background/src/secret-wasm/service.ts @@ -75,6 +75,19 @@ export class SecretWasmService { return utils.getTxEncryptionKey(nonce); } + async isNewApi(chainId: string): Promise { + const chainInfo = await this.chainsService.getChainInfoOrThrow(chainId); + + // XXX: Keplr should generate the seed deterministically according to the account. + // Otherwise, it will lost the encryption/decryption key if Keplr is uninstalled or local storage is cleared. + // For now, use the signature of some string to generate the seed. + // It need to more research. + const seed = await this.getSeed(chainInfo); + + const utils = this.getEnigmaUtils(chainInfo, seed); + return utils.isNewApi; + } + async encrypt( chainId: string, contractCodeHash: string, diff --git a/packages/proto-types/outputHash b/packages/proto-types/outputHash index 43b0dd3207..6c1d58ed7c 100644 --- a/packages/proto-types/outputHash +++ b/packages/proto-types/outputHash @@ -1 +1 @@ -47xVj6yH8GxPnu1BOIH1MiJVtlE6POLMYvtUZYTBu/5MPH+/VZKmDSx8zscGTLTJpxn9rxdR/+cPKXJdeaqXzulJEYlXTSU7x80PSUD7rlp2mWT0RYGFDIN+Fik5sFOv7Um1WRmAnzsEu+hDNatREyB47p1Gxms6JX0klLRyAyxUnUJRvDIjqQiAHvjtBGK0z5Fgtw08ei6MwIGQTf1GQHMO/FAQfd3uq8plIG5KNxinIvHEu7y1qZFBLB7UiMKcd8fWxfvYJA4IV73pCYbInRh3jcatX/QI80oBDMXBo9DCv5IaZQQs1G7JPcfzThPm \ No newline at end of file +47xVj6yH8GxPnu1BOIH1MiJVtlE6POLMYvtUZYTBu/5MPH+/VZKmDSx8zscGTLTJpxn9rxdR/+cPKXJdeaqXzulJEYlXTSU7x80PSUD7rlp2mWT0RYGFDIN+Fik5sFOv7Um1WRmAnzsEu+hDNatREyB47p1Gxms6JX0klLRyAyxUnUJRvDIjqQiAHvjtBGK0z5Fgtw08ei6MwIGQTf1GQHMO/FAQfd3uq8plIG5KNxinIvHEViD2YjIK7cmuvNIFc5CdQK0lzAoIV73pCYbInRh3jcatX/QI80oBDMXBo9DCv5IaZQQs1G7JPcfzThPm \ No newline at end of file diff --git a/packages/proto-types/proto-types-gen/proto/secret/compute/v1/genesis.proto b/packages/proto-types/proto-types-gen/proto/secret/compute/v1/genesis.proto new file mode 100644 index 0000000000..d18f000e5e --- /dev/null +++ b/packages/proto-types/proto-types-gen/proto/secret/compute/v1/genesis.proto @@ -0,0 +1,45 @@ +syntax = "proto3"; +package secret.compute.v1beta1; + +import "gogoproto/gogo.proto"; +import "secret/compute/v1beta1/types.proto"; + +option go_package = "github.com/scrtlabs/SecretNetwork/x/compute/internal/types"; + +// GenesisState - genesis state of x/wasm +message GenesisState { + // Params params = 1 [(gogoproto.nullable) = false]; + repeated Code codes = 2 + [ (gogoproto.nullable) = false, (gogoproto.jsontag) = "codes,omitempty" ]; + repeated Contract contracts = 3 [ + (gogoproto.nullable) = false, + (gogoproto.jsontag) = "contracts,omitempty" + ]; + repeated Sequence sequences = 4 [ + (gogoproto.nullable) = false, + (gogoproto.jsontag) = "sequences,omitempty" + ]; +} + +// Code struct encompasses CodeInfo and CodeBytes +message Code { + uint64 code_id = 1 [ (gogoproto.customname) = "CodeID" ]; + CodeInfo code_info = 2 [ (gogoproto.nullable) = false ]; + bytes code_bytes = 3; +} + +// Contract struct encompasses ContractAddress, ContractInfo, and ContractState +message Contract { + bytes contract_address = 1 + [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + ContractInfo contract_info = 2 [ (gogoproto.nullable) = false ]; + repeated Model contract_state = 3 [ (gogoproto.nullable) = false ]; + ContractCustomInfo contract_custom_info = 4; +} + +// Sequence id and value of a counter +message Sequence { + bytes id_key = 1 [ (gogoproto.customname) = "IDKey" ]; + uint64 value = 2; +} \ No newline at end of file diff --git a/packages/proto-types/proto-types-gen/proto/secret/compute/v1/msg.proto b/packages/proto-types/proto-types-gen/proto/secret/compute/v1/msg.proto new file mode 100644 index 0000000000..1996b8b55f --- /dev/null +++ b/packages/proto-types/proto-types-gen/proto/secret/compute/v1/msg.proto @@ -0,0 +1,189 @@ +syntax = "proto3"; +package secret.compute.v1; + +option go_package = "github.com/scrtlabs/SecretNetwork/x/compute/internal/types"; + +import "gogoproto/gogo.proto"; +import "cosmos_proto/cosmos.proto"; +import "cosmos/msg/v1/msg.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "amino/amino.proto"; + +// Msg defines the wasm Msg service. +service Msg { + option (cosmos.msg.v1.service) = true; + // StoreCode to submit Wasm code to the system + rpc StoreCode(MsgStoreCode) returns (MsgStoreCodeResponse); + // Instantiate creates a new smart contract instance for the given code id. + rpc InstantiateContract(MsgInstantiateContract) + returns (MsgInstantiateContractResponse); + // Execute submits the given message data to a smart contract + rpc ExecuteContract(MsgExecuteContract) returns (MsgExecuteContractResponse); + // Migrate runs a code upgrade/ downgrade for a smart contract + rpc MigrateContract(MsgMigrateContract) returns (MsgMigrateContractResponse); + // UpdateAdmin sets a new admin for a smart contract + rpc UpdateAdmin(MsgUpdateAdmin) returns (MsgUpdateAdminResponse); + // ClearAdmin removes any admin stored for a smart contract + rpc ClearAdmin(MsgClearAdmin) returns (MsgClearAdminResponse); +} + +message MsgStoreCode { + option (gogoproto.goproto_getters) = false; + option (cosmos.msg.v1.signer) = "sender"; + option (amino.name) = "wasm/MsgStoreCode"; + + // // sender is the canonical address of the sender + // bytes sender = 1 [ (gogoproto.casttype) = + // "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + // Sender is the actor that signed the messages + string sender = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; + // WASMByteCode can be raw or gzip compressed + bytes wasm_byte_code = 2 [ (gogoproto.customname) = "WASMByteCode" ]; + // Source is a valid absolute HTTPS URI to the contract's source code, + // optional + string source = 3; + // Builder is a valid docker image name with tag, optional + string builder = 4; +} + +// MsgStoreCodeResponse returns store result data. +message MsgStoreCodeResponse { + // CodeID is the reference to the stored WASM code + uint64 code_id = 1 [ (gogoproto.customname) = "CodeID" ]; +} + +message MsgInstantiateContract { + // option (gogoproto.goproto_getters) = false; + option (cosmos.msg.v1.signer) = "sender_address"; + option (amino.name) = "wasm/MsgInstantiateContract"; + + // sender is the canonical address of the sender + // string sender = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + string callback_code_hash = 2; + uint64 code_id = 3 [ (gogoproto.customname) = "CodeID" ]; + string label = 4; + // init_msg is an encrypted input to pass to the contract on init + bytes init_msg = 5; + repeated cosmos.base.v1beta1.Coin init_funds = 6 [ + (gogoproto.nullable) = false, + (amino.dont_omitempty) = true, + (amino.encoding) = "legacy_coins", + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" + ]; + // used internally for encryption, should always be empty in a signed + // transaction + bytes callback_sig = 7 [ (gogoproto.customname) = "CallbackSig" ]; + // Admin is an optional address that can execute migrations + string admin = 8; + // To get the message signer define sender_address as human adress for sender, + // as a work around without the need to modify cosmwasm valiation logic + string sender_address = 9 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; +} + +// MsgInstantiateContractResponse return instantiation result data +message MsgInstantiateContractResponse { + // Address is the bech32 address of the new contract instance. + string address = 1; + // Data contains base64-encoded bytes to returned from the contract + bytes data = 2; +} + +message MsgExecuteContract { + option (gogoproto.goproto_getters) = false; + option (cosmos.msg.v1.signer) = "sender_address"; + option (amino.name) = "wasm/MsgExecuteContract"; + + // sender is the canonical address of the sender + bytes sender = 1 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + // contract is the canonical address of the contract + bytes contract = 2 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + // msg is an encrypted input to pass to the contract on execute + bytes msg = 3; + // used internally for encryption, should always be empty in a signed + // transaction + string callback_code_hash = 4; + repeated cosmos.base.v1beta1.Coin sent_funds = 5 [ + (gogoproto.nullable) = false, + (amino.dont_omitempty) = true, + (amino.encoding) = "legacy_coins", + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" + ]; + // used internally for encryption, should always be empty in a signed + // transaction + bytes callback_sig = 6 [ (gogoproto.customname) = "CallbackSig" ]; + string sender_address = 7; +} + +// MsgExecuteContractResponse returns execution result data. +message MsgExecuteContractResponse { + // Data contains base64-encoded bytes to returned from the contract + bytes data = 1; +} + +// MsgMigrateContract runs a code upgrade/ downgrade for a smart contract +message MsgMigrateContract { + option (cosmos.msg.v1.signer) = "sender"; + option (amino.name) = "wasm/MsgMigrateContract"; + + // Sender is the that actor that signed the messages + string sender = 1; + // Contract is the address of the smart contract + string contract = 2; + // CodeID references the new WASM code + uint64 code_id = 3 [ (gogoproto.customname) = "CodeID" ]; + // msg is an encrypted input to pass to the contract on migration + bytes msg = 4; + // used internally for encryption, should always be empty in a signed + // transaction + bytes callback_sig = 7 [ (gogoproto.customname) = "CallbackSig" ]; + // used internally for encryption, should always be empty in a signed + // transaction + string callback_code_hash = 8; +} + +// MsgMigrateContractResponse returns contract migration result data. +message MsgMigrateContractResponse { + // Data contains same raw bytes returned as data from the wasm contract. + // (May be empty) + bytes data = 1; +} + +// MsgUpdateAdmin sets a new admin for a smart contract +message MsgUpdateAdmin { + option (cosmos.msg.v1.signer) = "sender"; + option (amino.name) = "wasm/MsgUpdateAdmin"; + + // Sender is the that actor that signed the messages + string sender = 1; + // NewAdmin address to be set + string new_admin = 2; + // Contract is the address of the smart contract + string contract = 3; + // used internally for encryption, should always be empty in a signed + // transaction + bytes callback_sig = 7 [ (gogoproto.customname) = "CallbackSig" ]; +} + +// MsgUpdateAdminResponse returns empty data +message MsgUpdateAdminResponse {} + +// MsgClearAdmin removes any admin stored for a smart contract +message MsgClearAdmin { + option (cosmos.msg.v1.signer) = "sender"; + option (amino.name) = "wasm/MsgClearAdmin"; + + // Sender is the that actor that signed the messages + string sender = 1; + // Contract is the address of the smart contract + string contract = 3; + // used internally for encryption, should always be empty in a signed + // transaction + bytes callback_sig = 7 [ (gogoproto.customname) = "CallbackSig" ]; +} + +// MsgClearAdminResponse returns empty data +message MsgClearAdminResponse {} diff --git a/packages/proto-types/proto-types-gen/proto/secret/compute/v1/query.proto b/packages/proto-types/proto-types-gen/proto/secret/compute/v1/query.proto new file mode 100644 index 0000000000..c88f62a8ae --- /dev/null +++ b/packages/proto-types/proto-types-gen/proto/secret/compute/v1/query.proto @@ -0,0 +1,172 @@ +syntax = "proto3"; +package secret.compute.v1beta1; + +import "gogoproto/gogo.proto"; +import "secret/compute/v1beta1/types.proto"; +import "google/protobuf/empty.proto"; +import "google/api/annotations.proto"; +import "cosmos/base/abci/v1beta1/abci.proto"; +import "cosmos/base/query/v1beta1/pagination.proto"; + +option go_package = "github.com/scrtlabs/SecretNetwork/x/compute/internal/types"; +option (gogoproto.goproto_getters_all) = false; +option (gogoproto.equal_all) = true; + +// Query defines the gRPC querier service +service Query { + // Query contract info by address + rpc ContractInfo(QueryByContractAddressRequest) + returns (QueryContractInfoResponse) { + option (google.api.http).get = "/compute/v1beta1/info/{contract_address}"; + } + // Query code info by id + rpc ContractsByCodeId(QueryByCodeIdRequest) + returns (QueryContractsByCodeIdResponse) { + option (google.api.http).get = "/compute/v1beta1/contracts/{code_id}"; + } + // Query secret contract + rpc QuerySecretContract(QuerySecretContractRequest) + returns (QuerySecretContractResponse) { + option (google.api.http).get = "/compute/v1beta1/query/{contract_address}"; + } + // Query a specific contract code by id + rpc Code(QueryByCodeIdRequest) returns (QueryCodeResponse) { + option (google.api.http).get = "/compute/v1beta1/code/{code_id}"; + } + // Query all contract codes on-chain + rpc Codes(google.protobuf.Empty) returns (QueryCodesResponse) { + option (google.api.http).get = "/compute/v1beta1/codes"; + } + // Query code hash by contract address + rpc CodeHashByContractAddress(QueryByContractAddressRequest) + returns (QueryCodeHashResponse) { + option (google.api.http).get = + "/compute/v1beta1/code_hash/by_contract_address/{contract_address}"; + } + // Query code hash by code id + rpc CodeHashByCodeId(QueryByCodeIdRequest) returns (QueryCodeHashResponse) { + option (google.api.http).get = + "/compute/v1beta1/code_hash/by_code_id/{code_id}"; + } + // Query contract label by address + rpc LabelByAddress(QueryByContractAddressRequest) + returns (QueryContractLabelResponse) { + option (google.api.http).get = "/compute/v1beta1/label/{contract_address}"; + } + // Query contract address by label + rpc AddressByLabel(QueryByLabelRequest) + returns (QueryContractAddressResponse) { + option (google.api.http).get = "/compute/v1beta1/contract_address/{label}"; + } + // ContractHistory gets the contract code history + rpc ContractHistory(QueryContractHistoryRequest) + returns (QueryContractHistoryResponse) { + option (google.api.http).get = + "/compute/v1beta1/contract_history/{contract_address}"; + } +} + +message QuerySecretContractRequest { + // address is the bech32 human readable address of the contract + string contract_address = 1; + bytes query = 2; +} + +message QueryByLabelRequest { string label = 1; } + +message QueryByContractAddressRequest { + // address is the bech32 human readable address of the contract + string contract_address = 1; +} + +message QueryByCodeIdRequest { uint64 code_id = 1; } + +message QuerySecretContractResponse { bytes data = 1; } + +// QueryContractInfoResponse is the response type for the Query/ContractInfo RPC +// method +message QueryContractInfoResponse { + // contract_address is the bech32 human readable address of the contract + string contract_address = 1; + ContractInfo contract_info = 2 + [ (gogoproto.embed) = true, (gogoproto.jsontag) = "" ]; +} + +// ContractInfoWithAddress adds the contract address to the ContractInfo +// representation +message ContractInfoWithAddress { + // contract_address is the bech32 human readable address of the contract + string contract_address = 1; + ContractInfo contract_info = 2 + [ (gogoproto.embed) = true, (gogoproto.jsontag) = "" ]; +} + +message QueryContractsByCodeIdResponse { + repeated ContractInfoWithAddress contract_infos = 1 + [ (gogoproto.nullable) = false ]; +} + +message CodeInfoResponse { + uint64 code_id = 1; + // creator is the bech32 human readable address of the contract + string creator = 2; + string code_hash = 3; + string source = 4; + string builder = 5; +} + +message QueryCodeResponse { + CodeInfoResponse code_info = 1 + [ (gogoproto.embed) = true, (gogoproto.jsontag) = "" ]; + bytes wasm = 2; +} + +message QueryCodesResponse { + repeated CodeInfoResponse code_infos = 1 [ (gogoproto.nullable) = false ]; +} + +message QueryContractAddressResponse { + // address is the bech32 human readable address of the contract + string contract_address = 1; +} + +message QueryContractLabelResponse { string label = 1; } + +message QueryCodeHashResponse { string code_hash = 1; } + +// DecryptedAnswer is a struct that represents a decrypted tx-query +message DecryptedAnswer { + option (gogoproto.equal) = false; + + string type = 1; + string input = 2; + string output_data = 3; + string output_data_as_string = 4; +} + +message DecryptedAnswers { + option (gogoproto.equal) = false; + + repeated DecryptedAnswer answers = 1; + repeated cosmos.base.abci.v1beta1.StringEvent output_logs = 2 + [ (gogoproto.nullable) = false ]; + string output_error = 3; + string plaintext_error = 4; +} + +// QueryContractHistoryRequest is the request type for the Query/ContractHistory +// RPC method +message QueryContractHistoryRequest { + option (gogoproto.equal) = false; + // address is the address of the contract to query + string contract_address = 1; +} + +// QueryContractHistoryResponse is the response type for the +// Query/ContractHistory RPC method +message QueryContractHistoryResponse { + option (gogoproto.equal) = false; + + repeated ContractCodeHistoryEntry entries = 1 + [ (gogoproto.nullable) = false ]; +} \ No newline at end of file diff --git a/packages/proto-types/proto-types-gen/proto/secret/compute/v1/types.proto b/packages/proto-types/proto-types-gen/proto/secret/compute/v1/types.proto new file mode 100644 index 0000000000..6349d7420d --- /dev/null +++ b/packages/proto-types/proto-types-gen/proto/secret/compute/v1/types.proto @@ -0,0 +1,110 @@ +syntax = "proto3"; +package secret.compute.v1beta1; + +import "gogoproto/gogo.proto"; + +option go_package = "github.com/scrtlabs/SecretNetwork/x/compute/internal/types"; +option (gogoproto.goproto_getters_all) = false; +option (gogoproto.equal_all) = true; + +enum AccessType { + option (gogoproto.goproto_enum_prefix) = false; + option (gogoproto.goproto_enum_stringer) = false; + UNDEFINED = 0 [ (gogoproto.enumvalue_customname) = "AccessTypeUndefined" ]; + NOBODY = 1 [ (gogoproto.enumvalue_customname) = "AccessTypeNobody" ]; + ONLY_ADDRESS = 2 + [ (gogoproto.enumvalue_customname) = "AccessTypeOnlyAddress" ]; + EVERYBODY = 3 [ (gogoproto.enumvalue_customname) = "AccessTypeEverybody" ]; +} + +message AccessTypeParam { + option (gogoproto.goproto_stringer) = true; + AccessType value = 1 [ (gogoproto.moretags) = "yaml:\"value\"" ]; +} + +// CodeInfo is data for the uploaded contract WASM code +message CodeInfo { + bytes code_hash = 1; + bytes creator = 2 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + string source = 3; + string builder = 4; +} + +message ContractKey { + bytes og_contract_key = 1; + bytes current_contract_key = 2; + bytes current_contract_key_proof = 3; +} + +message ContractCustomInfo { + ContractKey enclave_key = 1 [ (gogoproto.customname) = "EnclaveKey" ]; + string label = 2; +} + +// ContractInfo stores a WASM contract instance +message ContractInfo { + // CodeID is the reference to the stored Wasm code + uint64 code_id = 1 [ (gogoproto.customname) = "CodeID" ]; + // Creator address who initially instantiated the contract + bytes creator = 2 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + // Label is mandatory metadata to be stored with a contract instance. + string label = 4; + // Created Tx position when the contract was instantiated. + AbsoluteTxPosition created = 5; + string ibc_port_id = 6 [ (gogoproto.customname) = "IBCPortID" ]; + // Admin is an optional address that can execute migrations + string admin = 7; + // Proof that enclave executed the instantiate command + bytes admin_proof = 8; +} + +// AbsoluteTxPosition can be used to sort contracts +message AbsoluteTxPosition { + // BlockHeight is the block the contract was created at + int64 block_height = 1; + // TxIndex is a monotonic counter within the block (actual transaction index, + // or gas consumed) + uint64 tx_index = 2; +} + +// Model is a struct that holds a KV pair +message Model { + // hex-encode key to read it better (this is often ascii) + bytes Key = 1 [ (gogoproto.casttype) = + "github.com/cometbft/cometbft/libs/bytes.HexBytes" ]; + // base64-encode raw value + bytes Value = 2; +} + +// ContractCodeHistoryOperationType actions that caused a code change +enum ContractCodeHistoryOperationType { + option (gogoproto.goproto_enum_prefix) = false; + // ContractCodeHistoryOperationTypeUnspecified placeholder for empty value + CONTRACT_CODE_HISTORY_OPERATION_TYPE_UNSPECIFIED = 0 + [ (gogoproto.enumvalue_customname) = + "ContractCodeHistoryOperationTypeUnspecified" ]; + // ContractCodeHistoryOperationTypeInit on chain contract instantiation + CONTRACT_CODE_HISTORY_OPERATION_TYPE_INIT = 1 + [ (gogoproto.enumvalue_customname) = + "ContractCodeHistoryOperationTypeInit" ]; + // ContractCodeHistoryOperationTypeMigrate code migration + CONTRACT_CODE_HISTORY_OPERATION_TYPE_MIGRATE = 2 + [ (gogoproto.enumvalue_customname) = + "ContractCodeHistoryOperationTypeMigrate" ]; + // ContractCodeHistoryOperationTypeGenesis based on genesis data + CONTRACT_CODE_HISTORY_OPERATION_TYPE_GENESIS = 3 + [ (gogoproto.enumvalue_customname) = + "ContractCodeHistoryOperationTypeGenesis" ]; +} + +// ContractCodeHistoryEntry metadata to a contract. +message ContractCodeHistoryEntry { + ContractCodeHistoryOperationType operation = 1; + // CodeID is the reference to the stored WASM code + uint64 code_id = 2 [ (gogoproto.customname) = "CodeID" ]; + // Updated Tx position when the operation was executed. + AbsoluteTxPosition updated = 3; + bytes msg = 4; +} \ No newline at end of file diff --git a/packages/proto-types/proto-types-gen/proto/secret/registration/v1/genesis.proto b/packages/proto-types/proto-types-gen/proto/secret/registration/v1/genesis.proto new file mode 100644 index 0000000000..8a30bb3a4f --- /dev/null +++ b/packages/proto-types/proto-types-gen/proto/secret/registration/v1/genesis.proto @@ -0,0 +1,17 @@ +syntax = "proto3"; +package secret.registration.v1beta1; + +import "gogoproto/gogo.proto"; +import "secret/registration/v1beta1/types.proto"; +import "secret/registration/v1beta1/msg.proto"; + +option go_package = "github.com/scrtlabs/SecretNetwork/x/registration/internal/types"; +option (gogoproto.goproto_getters_all) = false; +option (gogoproto.equal_all) = true; + +message GenesisState { + repeated RegistrationNodeInfo registration = 1 + [ (gogoproto.jsontag) = "reg_info" ]; + MasterKey node_exch_master_key = 2 [ (gogoproto.jsontag) = "node_exch_key" ]; + MasterKey io_master_key = 3 [ (gogoproto.jsontag) = "io_exch_key" ]; +} \ No newline at end of file diff --git a/packages/proto-types/proto-types-gen/proto/secret/registration/v1/msg.proto b/packages/proto-types/proto-types-gen/proto/secret/registration/v1/msg.proto new file mode 100644 index 0000000000..e60c0ddb49 --- /dev/null +++ b/packages/proto-types/proto-types-gen/proto/secret/registration/v1/msg.proto @@ -0,0 +1,45 @@ +syntax = "proto3"; +package secret.registration.v1beta1; + +import "gogoproto/gogo.proto"; +import "cosmos_proto/cosmos.proto"; +import "cosmos/msg/v1/msg.proto"; +import "cosmos/base/v1beta1/coin.proto"; + +option go_package = "github.com/scrtlabs/SecretNetwork/x/registration/internal/types"; +option (gogoproto.goproto_getters_all) = false; +option (gogoproto.equal_all) = true; + +// Msg defines the wasm Msg service. +service Msg { + option (cosmos.msg.v1.service) = true; + // Register and authenticate new node + rpc RegisterAuth(RaAuthenticate) returns (RaAuthenticateResponse); +} + +message RaAuthenticate { + option (gogoproto.goproto_getters) = false; + option (cosmos.msg.v1.signer) = "sender"; + + // bytes sender = 1 [ (gogoproto.casttype) = + // "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; + string sender = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; + bytes certificate = 2 [ + (gogoproto.casttype) = "github.com/scrtlabs/SecretNetwork/x/registration/" + "remote_attestation.Certificate", + (gogoproto.jsontag) = "ra_cert" + ]; + // string sender_address = 3 [ (cosmos_proto.scalar) = "cosmos.AddressString" + // ]; + bytes sender_addr = 3 [ (gogoproto.casttype) = + "github.com/cosmos/cosmos-sdk/types.AccAddress" ]; +} + +message RaAuthenticateResponse { + string data = 1; + string events = 2; +} + +message MasterKey { bytes bytes = 1; } + +message Key { bytes key = 1 [ (gogoproto.jsontag) = "key" ]; } diff --git a/packages/proto-types/proto-types-gen/proto/secret/registration/v1/query.proto b/packages/proto-types/proto-types-gen/proto/secret/registration/v1/query.proto new file mode 100644 index 0000000000..72f69fca4c --- /dev/null +++ b/packages/proto-types/proto-types-gen/proto/secret/registration/v1/query.proto @@ -0,0 +1,37 @@ +syntax = "proto3"; +package secret.registration.v1beta1; + +import "gogoproto/gogo.proto"; +import "google/protobuf/empty.proto"; +import "google/api/annotations.proto"; +import "secret/registration/v1beta1/msg.proto"; +import "secret/registration/v1beta1/genesis.proto"; + +option go_package = "github.com/scrtlabs/SecretNetwork/x/registration/internal/types"; +option (gogoproto.goproto_getters_all) = false; +option (gogoproto.equal_all) = true; + +// Query provides defines the gRPC querier service +service Query { + // Returns the key used for transactions + rpc TxKey(google.protobuf.Empty) returns (Key) { + option (google.api.http).get = "/registration/v1beta1/tx-key"; + } + // Returns the key used for registration + rpc RegistrationKey(google.protobuf.Empty) returns (Key) { + option (google.api.http).get = "/registration/v1beta1/registration-key"; + } + + // Returns the encrypted seed for a registered node by public key + rpc EncryptedSeed(QueryEncryptedSeedRequest) + returns (QueryEncryptedSeedResponse) { + option (google.api.http).get = + "/registration/v1beta1/encrypted-seed/{pub_key}"; + } +} + +message QueryEncryptedSeedRequest { bytes pub_key = 1; } + +message QueryEncryptedSeedResponse { + bytes encrypted_seed = 1; // [(gogoproto.nullable) = false]; +} diff --git a/packages/proto-types/proto-types-gen/proto/secret/registration/v1/remote_attestation/types.proto b/packages/proto-types/proto-types-gen/proto/secret/registration/v1/remote_attestation/types.proto new file mode 100644 index 0000000000..0704095100 --- /dev/null +++ b/packages/proto-types/proto-types-gen/proto/secret/registration/v1/remote_attestation/types.proto @@ -0,0 +1,58 @@ +syntax = "proto3"; +package secret.registration.remote_attestation.v1beta1; + +import "gogoproto/gogo.proto"; + +option go_package = "github.com/scrtlabs/SecretNetwork/x/registration/remote_attestation"; +option (gogoproto.goproto_getters_all) = false; +option (gogoproto.equal_all) = true; + +message QuoteReport { + string id = 1 [ (gogoproto.customname) = "ID" ]; + string timestamp = 2; + uint64 version = 3; + string isv_enclave_quote_status = 4 + [ (gogoproto.jsontag) = "isvEnclaveQuoteStatus" ]; + string platform_info_blob = 5 [ (gogoproto.jsontag) = "platformInfoBlob" ]; + string isv_enclave_quote_body = 6 + [ (gogoproto.jsontag) = "isvEnclaveQuoteBody" ]; + repeated string advisory_ids = 7 [ + (gogoproto.customname) = "AdvisoryIDs", + (gogoproto.jsontag) = "advisoryIDs" + ]; +} + +message QuoteReportBody { + string mr_enclave = 1; + string mr_signer = 2; + string report_data = 3; +} + +message QuoteReportData { + uint64 version = 1; + uint64 sign_type = 2; + QuoteReportBody report_body = 3; +} + +message EndorsedAttestationReport { + bytes report = 1; + bytes signature = 2; + bytes signing_cert = 3; +} + +message SGXEC256Signature { + string gx = 1; + string gy = 2; +} + +message PlatformInfoBlob { + uint32 sgx_epid_group_flags = 1; + uint32 sgx_tcb_evaluation_flags = 2; + uint32 pse_evaluation_flags = 3; + string latest_equivalent_tcb_psvn = 4; + string latest_pse_isvsvn = 5; + string latest_psda_svn = 6; + uint32 xeid = 7; + uint32 gid = 8; + SGXEC256Signature sgx_ec256_signature_t = 9; +} diff --git a/packages/proto-types/proto-types-gen/proto/secret/registration/v1/types.proto b/packages/proto-types/proto-types-gen/proto/secret/registration/v1/types.proto new file mode 100644 index 0000000000..0da5e2f0b3 --- /dev/null +++ b/packages/proto-types/proto-types-gen/proto/secret/registration/v1/types.proto @@ -0,0 +1,26 @@ +syntax = "proto3"; +package secret.registration.v1beta1; + +import "gogoproto/gogo.proto"; + +option go_package = "github.com/scrtlabs/SecretNetwork/x/registration/internal/types"; +option (gogoproto.goproto_getters_all) = false; +option (gogoproto.equal_all) = true; + +message SeedConfig { + string master_key = 1 [ (gogoproto.jsontag) = "pk2" ]; + string encrypted_key = 2 [ (gogoproto.jsontag) = "encKey" ]; + uint32 version = 3 [ (gogoproto.jsontag) = "version" ]; +} + +message LegacySeedConfig { + string master_cert = 1 [ (gogoproto.jsontag) = "pk" ]; + string encrypted_key = 2 [ (gogoproto.jsontag) = "encKey" ]; +} + +message RegistrationNodeInfo { + bytes certificate = 1 + [ (gogoproto.casttype) = "github.com/scrtlabs/SecretNetwork/x/" + "registration/remote_attestation.Certificate" ]; + bytes encrypted_seed = 2; +} diff --git a/packages/proto-types/proto-types-gen/scripts/proto-gen.mjs b/packages/proto-types/proto-types-gen/scripts/proto-gen.mjs index e955163e8c..9bd013f5af 100644 --- a/packages/proto-types/proto-types-gen/scripts/proto-gen.mjs +++ b/packages/proto-types/proto-types-gen/scripts/proto-gen.mjs @@ -116,6 +116,7 @@ function setOutputHash(root, hash) { "ibc/applications/fee/v1/fee.proto", "ibc/applications/fee/v1/tx.proto", "secret/compute/v1beta1/msg.proto", + "secret/compute/v1/msg.proto", "ethermint/types/v1/web3.proto", "stride/stakeibc/validator.proto", "stride/stakeibc/tx.proto", diff --git a/packages/provider-extension/src/enigma.ts b/packages/provider-extension/src/enigma.ts index 7f993d4411..77fde485ca 100644 --- a/packages/provider-extension/src/enigma.ts +++ b/packages/provider-extension/src/enigma.ts @@ -9,6 +9,10 @@ export class KeplrEnigmaUtils implements SecretUtils { protected readonly keplr: Keplr ) {} + async isNewApi(): Promise { + return await this.keplr.enigmaIsNewApi(this.chainId); + } + async getPubkey(): Promise { return await this.keplr.getEnigmaPubKey(this.chainId); } diff --git a/packages/provider-extension/src/keplr.ts b/packages/provider-extension/src/keplr.ts index a47be15cd0..1f224eb085 100644 --- a/packages/provider-extension/src/keplr.ts +++ b/packages/provider-extension/src/keplr.ts @@ -498,6 +498,10 @@ export class Keplr implements IKeplr { ]); } + async enigmaIsNewApi(chainId: string): Promise { + return await Keplr.requestMethod("enigmaIsNewApi", [chainId]); + } + getEnigmaUtils(chainId: string): SecretUtils { if (this.enigmaUtils.has(chainId)) { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion diff --git a/packages/provider-mock/src/mock.ts b/packages/provider-mock/src/mock.ts index cf5539b804..294a952244 100644 --- a/packages/provider-mock/src/mock.ts +++ b/packages/provider-mock/src/mock.ts @@ -95,6 +95,10 @@ export class MockKeplr implements Keplr { throw new Error("Not implemented"); } + enigmaIsNewApi(): Promise { + throw new Error("Not implemented"); + } + experimentalSuggestChain(): Promise { throw new Error("Not implemented"); } diff --git a/packages/provider/src/core.ts b/packages/provider/src/core.ts index 75990248dc..7d8d592b34 100644 --- a/packages/provider/src/core.ts +++ b/packages/provider/src/core.ts @@ -756,6 +756,18 @@ export class Keplr implements IKeplr, KeplrCoreTypes { }); } + async enigmaIsNewApi(chainId: string): Promise { + return await sendSimpleMessage( + this.requester, + BACKGROUND_PORT, + "secret-wasm", + "is-new-api-msg", + { + chainId, + } + ); + } + async enigmaEncrypt( chainId: string, contractCodeHash: string, diff --git a/packages/provider/src/enigma.ts b/packages/provider/src/enigma.ts index 7f993d4411..77fde485ca 100644 --- a/packages/provider/src/enigma.ts +++ b/packages/provider/src/enigma.ts @@ -9,6 +9,10 @@ export class KeplrEnigmaUtils implements SecretUtils { protected readonly keplr: Keplr ) {} + async isNewApi(): Promise { + return await this.keplr.enigmaIsNewApi(this.chainId); + } + async getPubkey(): Promise { return await this.keplr.getEnigmaPubKey(this.chainId); } diff --git a/packages/provider/src/inject.ts b/packages/provider/src/inject.ts index 579f8a2600..25d79fa096 100644 --- a/packages/provider/src/inject.ts +++ b/packages/provider/src/inject.ts @@ -778,6 +778,10 @@ export class InjectedKeplr implements IKeplr, KeplrCoreTypes { ]); } + async enigmaIsNewApi(chainId: string): Promise { + return await this.requestMethod("enigmaIsNewApi", [chainId]); + } + getEnigmaUtils(chainId: string): SecretUtils { if (this.enigmaUtils.has(chainId)) { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion diff --git a/packages/stores/src/account/secret.ts b/packages/stores/src/account/secret.ts index 99f5f5b1f4..adb6175cfa 100644 --- a/packages/stores/src/account/secret.ts +++ b/packages/stores/src/account/secret.ts @@ -4,10 +4,16 @@ import { Buffer } from "buffer/"; import { CoinPrimitive } from "../common"; import { ChainGetter } from "../chain"; import { DenomHelper } from "@keplr-wallet/common"; -import { MsgExecuteContract } from "@keplr-wallet/proto-types/secret/compute/v1beta1/msg"; +import { MsgExecuteContract as MsgExecuteContractV1Beta1 } from "@keplr-wallet/proto-types/secret/compute/v1beta1/msg"; +import { MsgExecuteContract as MsgExecuteContractV1 } from "@keplr-wallet/proto-types/secret/compute/v1/msg"; import { Bech32Address } from "@keplr-wallet/cosmos"; import { Dec, DecUtils } from "@keplr-wallet/unit"; -import { AppCurrency, KeplrSignOptions, StdFee } from "@keplr-wallet/types"; +import { + AppCurrency, + KeplrSignOptions, + StdFee, + SecretUtils, +} from "@keplr-wallet/types"; import { DeepPartial, DeepReadonly, Optional } from "utility-types"; import { CosmosAccount } from "./cosmos"; import deepmerge from "deepmerge"; @@ -214,30 +220,66 @@ export class SecretAccountImpl { return this.base.cosmos.makeTx( type, async () => { + const keplr = await this.base.getKeplr(); + if (!keplr) { + throw new Error("Can't get the Keplr API"); + } + + const enigmaUtils = keplr.getEnigmaUtils(this.chainId); + encryptedMsg = await this.encryptSecretContractMsg( contractAddress, - obj + obj, + enigmaUtils ); - const msg = { - type: this.msgOpts.executeSecretWasm.type, - value: { - sender: this.base.bech32Address, - contract: contractAddress, - // callback_code_hash: "", - msg: Buffer.from(encryptedMsg).toString("base64"), - sent_funds: sentFunds, - // callback_sig: null, - }, - }; - - return { - aminoMsgs: [msg], - protoMsgs: [ - { + const msg = (await enigmaUtils.isNewApi()) + ? { + type: this.msgOpts.executeSecretWasm.type, + value: { + sender_address: this.base.bech32Address, + sender: Buffer.from( + Bech32Address.fromBech32(this.base.bech32Address).address + ).toString("base64"), + contract: Buffer.from( + Bech32Address.fromBech32(contractAddress).address + ).toString("base64"), + // callback_code_hash: "", + msg: Buffer.from(encryptedMsg).toString("base64"), + sent_funds: sentFunds, + // callback_sig: null, + }, + } + : { + type: this.msgOpts.executeSecretWasm.type, + value: { + sender: this.base.bech32Address, + contract: contractAddress, + // callback_code_hash: "", + msg: Buffer.from(encryptedMsg).toString("base64"), + sent_funds: sentFunds, + // callback_sig: null, + }, + }; + + const protoMsg = (await enigmaUtils.isNewApi()) + ? { + // type url must be with v1beta1 + typeUrl: "/secret.compute.v1beta1.MsgExecuteContract", + value: MsgExecuteContractV1.encode( + MsgExecuteContractV1.fromPartial({ + sender: Buffer.from(msg.value.sender, "base64"), + senderAddress: msg.value.sender_address, + contract: Buffer.from(msg.value.contract, "base64"), + msg: Buffer.from(msg.value.msg, "base64"), + sentFunds: msg.value.sent_funds, + }) + ).finish(), + } + : { typeUrl: "/secret.compute.v1beta1.MsgExecuteContract", - value: MsgExecuteContract.encode( - MsgExecuteContract.fromPartial({ + value: MsgExecuteContractV1Beta1.encode( + MsgExecuteContractV1Beta1.fromPartial({ sender: Bech32Address.fromBech32(msg.value.sender).address, contract: Bech32Address.fromBech32(msg.value.contract) .address, @@ -245,8 +287,11 @@ export class SecretAccountImpl { sentFunds: msg.value.sent_funds, }) ).finish(), - }, - ], + }; + + return { + aminoMsgs: [msg], + protoMsgs: [protoMsg], }; }, preOnTxEvents @@ -278,30 +323,66 @@ export class SecretAccountImpl { await this.base.cosmos.sendMsgs( type, async () => { + const keplr = await this.base.getKeplr(); + if (!keplr) { + throw new Error("Can't get the Keplr API"); + } + + const enigmaUtils = keplr.getEnigmaUtils(this.chainId); + encryptedMsg = await this.encryptSecretContractMsg( contractAddress, - obj + obj, + enigmaUtils ); - const msg = { - type: this.msgOpts.executeSecretWasm.type, - value: { - sender: this.base.bech32Address, - contract: contractAddress, - // callback_code_hash: "", - msg: Buffer.from(encryptedMsg).toString("base64"), - sent_funds: sentFunds, - // callback_sig: null, - }, - }; - - return { - aminoMsgs: [msg], - protoMsgs: [ - { + const msg = (await enigmaUtils.isNewApi()) + ? { + type: this.msgOpts.executeSecretWasm.type, + value: { + sender_address: this.base.bech32Address, + sender: Buffer.from( + Bech32Address.fromBech32(this.base.bech32Address).address + ).toString("base64"), + contract: Buffer.from( + Bech32Address.fromBech32(contractAddress).address + ).toString("base64"), + // callback_code_hash: "", + msg: Buffer.from(encryptedMsg).toString("base64"), + sent_funds: sentFunds, + // callback_sig: null, + }, + } + : { + type: this.msgOpts.executeSecretWasm.type, + value: { + sender: this.base.bech32Address, + contract: contractAddress, + // callback_code_hash: "", + msg: Buffer.from(encryptedMsg).toString("base64"), + sent_funds: sentFunds, + // callback_sig: null, + }, + }; + + const protoMsg = (await enigmaUtils.isNewApi()) + ? { + // type url must be with v1beta1 typeUrl: "/secret.compute.v1beta1.MsgExecuteContract", - value: MsgExecuteContract.encode( - MsgExecuteContract.fromPartial({ + value: MsgExecuteContractV1.encode( + MsgExecuteContractV1.fromPartial({ + sender: Buffer.from(msg.value.sender, "base64"), + senderAddress: msg.value.sender_address, + contract: Buffer.from(msg.value.contract, "base64"), + msg: Buffer.from(msg.value.msg, "base64"), + sentFunds: msg.value.sent_funds, + }) + ).finish(), + } + : { + typeUrl: "/secret.compute.v1beta1.MsgExecuteContract", + value: MsgExecuteContractV1Beta1.encode( + MsgExecuteContractV1Beta1.fromPartial({ sender: Bech32Address.fromBech32(msg.value.sender).address, contract: Bech32Address.fromBech32(msg.value.contract) .address, @@ -309,8 +390,11 @@ export class SecretAccountImpl { sentFunds: msg.value.sent_funds, }) ).finish(), - }, - ], + }; + + return { + aminoMsgs: [msg], + protoMsgs: [protoMsg], }; }, memo, @@ -329,7 +413,8 @@ export class SecretAccountImpl { protected async encryptSecretContractMsg( contractAddress: string, // eslint-disable-next-line @typescript-eslint/ban-types - obj: object + obj: object, + enigmaUtils: SecretUtils ): Promise { const queryContractCodeHashResponse = await this.queries.secret.querySecretContractCodeHash @@ -344,12 +429,6 @@ export class SecretAccountImpl { const contractCodeHash = queryContractCodeHashResponse.data.code_hash; - const keplr = await this.base.getKeplr(); - if (!keplr) { - throw new Error("Can't get the Keplr API"); - } - - const enigmaUtils = keplr.getEnigmaUtils(this.chainId); return await enigmaUtils.encrypt(contractCodeHash, obj); } diff --git a/packages/types/src/secretjs.ts b/packages/types/src/secretjs.ts index 1c1c23cd43..9b73049215 100644 --- a/packages/types/src/secretjs.ts +++ b/packages/types/src/secretjs.ts @@ -1,4 +1,5 @@ export interface SecretUtils { + isNewApi: () => Promise; getPubkey: () => Promise; decrypt: (ciphertext: Uint8Array, nonce: Uint8Array) => Promise; encrypt: ( diff --git a/packages/types/src/wallet/keplr.ts b/packages/types/src/wallet/keplr.ts index 676e53c24e..4a2e18363c 100644 --- a/packages/types/src/wallet/keplr.ts +++ b/packages/types/src/wallet/keplr.ts @@ -202,6 +202,7 @@ export interface Keplr { ciphertext: Uint8Array, nonce: Uint8Array ): Promise; + enigmaIsNewApi(chainId: string): Promise; /** * Sign the sign doc with ethermint's EIP-712 format. diff --git a/packages/wc-client/src/index.ts b/packages/wc-client/src/index.ts index c962bfe551..3927eb1ed4 100644 --- a/packages/wc-client/src/index.ts +++ b/packages/wc-client/src/index.ts @@ -425,6 +425,10 @@ export class KeplrWalletConnectV2 implements Keplr { throw new Error("Not yet implemented"); } + enigmaIsNewApi(_chainId: string): Promise { + throw new Error("Not yet implemented"); + } + experimentalSignEIP712CosmosTx_v0( _chainId: string, _signer: string,