From 8df918395ffab8a94b737ceb35e1291499738b1f Mon Sep 17 00:00:00 2001 From: 0xxCodemonkey Date: Sat, 10 Sep 2022 14:46:37 +0200 Subject: [PATCH 01/31] Update protos and api --- src/Api/Protos/google/protobuf/any.proto | 2 +- src/Api/Protos/google/protobuf/duration.proto | 116 +++++++++++ src/Api/Protos/google/protobuf/empty.proto | 51 +++++ .../Protos/google/protobuf/timestamp.proto | 138 ++++++++++++ .../Protos/secret/compute/v1beta1/msg.proto | 107 +++------- .../Protos/secret/compute/v1beta1/query.proto | 196 ++++++++---------- .../Protos/secret/compute/v1beta1/types.proto | 51 +---- src/Query/ComputeQueryClient.cs | 24 +-- src/SecretNET.csproj | 3 +- 9 files changed, 444 insertions(+), 244 deletions(-) create mode 100644 src/Api/Protos/google/protobuf/duration.proto create mode 100644 src/Api/Protos/google/protobuf/empty.proto create mode 100644 src/Api/Protos/google/protobuf/timestamp.proto diff --git a/src/Api/Protos/google/protobuf/any.proto b/src/Api/Protos/google/protobuf/any.proto index 58b51158..f0c81958 100644 --- a/src/Api/Protos/google/protobuf/any.proto +++ b/src/Api/Protos/google/protobuf/any.proto @@ -161,4 +161,4 @@ message Any { option (gogoproto.stringer) = false; } -option (gogoproto.goproto_registration) = false; +option (gogoproto.goproto_registration) = false; \ No newline at end of file diff --git a/src/Api/Protos/google/protobuf/duration.proto b/src/Api/Protos/google/protobuf/duration.proto new file mode 100644 index 00000000..b6f4b027 --- /dev/null +++ b/src/Api/Protos/google/protobuf/duration.proto @@ -0,0 +1,116 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option cc_enable_arenas = true; +option go_package = "types"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "DurationProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; + +// A Duration represents a signed, fixed-length span of time represented +// as a count of seconds and fractions of seconds at nanosecond +// resolution. It is independent of any calendar and concepts like "day" +// or "month". It is related to Timestamp in that the difference between +// two Timestamp values is a Duration and it can be added or subtracted +// from a Timestamp. Range is approximately +-10,000 years. +// +// # Examples +// +// Example 1: Compute Duration from two Timestamps in pseudo code. +// +// Timestamp start = ...; +// Timestamp end = ...; +// Duration duration = ...; +// +// duration.seconds = end.seconds - start.seconds; +// duration.nanos = end.nanos - start.nanos; +// +// if (duration.seconds < 0 && duration.nanos > 0) { +// duration.seconds += 1; +// duration.nanos -= 1000000000; +// } else if (durations.seconds > 0 && duration.nanos < 0) { +// duration.seconds -= 1; +// duration.nanos += 1000000000; +// } +// +// Example 2: Compute Timestamp from Timestamp + Duration in pseudo code. +// +// Timestamp start = ...; +// Duration duration = ...; +// Timestamp end = ...; +// +// end.seconds = start.seconds + duration.seconds; +// end.nanos = start.nanos + duration.nanos; +// +// if (end.nanos < 0) { +// end.seconds -= 1; +// end.nanos += 1000000000; +// } else if (end.nanos >= 1000000000) { +// end.seconds += 1; +// end.nanos -= 1000000000; +// } +// +// Example 3: Compute Duration from datetime.timedelta in Python. +// +// td = datetime.timedelta(days=3, minutes=10) +// duration = Duration() +// duration.FromTimedelta(td) +// +// # JSON Mapping +// +// In JSON format, the Duration type is encoded as a string rather than an +// object, where the string ends in the suffix "s" (indicating seconds) and +// is preceded by the number of seconds, with nanoseconds expressed as +// fractional seconds. For example, 3 seconds with 0 nanoseconds should be +// encoded in JSON format as "3s", while 3 seconds and 1 nanosecond should +// be expressed in JSON format as "3.000000001s", and 3 seconds and 1 +// microsecond should be expressed in JSON format as "3.000001s". +// +// +message Duration { + // Signed seconds of the span of time. Must be from -315,576,000,000 + // to +315,576,000,000 inclusive. Note: these bounds are computed from: + // 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years + int64 seconds = 1; + + // Signed fractions of a second at nanosecond resolution of the span + // of time. Durations less than one second are represented with a 0 + // `seconds` field and a positive or negative `nanos` field. For durations + // of one second or more, a non-zero value for the `nanos` field must be + // of the same sign as the `seconds` field. Must be from -999,999,999 + // to +999,999,999 inclusive. + int32 nanos = 2; +} \ No newline at end of file diff --git a/src/Api/Protos/google/protobuf/empty.proto b/src/Api/Protos/google/protobuf/empty.proto new file mode 100644 index 00000000..51ef775e --- /dev/null +++ b/src/Api/Protos/google/protobuf/empty.proto @@ -0,0 +1,51 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option go_package = "google.golang.org/protobuf/types/known/emptypb"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "EmptyProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; +option cc_enable_arenas = true; + +// A generic empty message that you can re-use to avoid defining duplicated +// empty messages in your APIs. A typical example is to use it as the request +// or the response type of an API method. For instance: +// +// service Foo { +// rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); +// } +// +message Empty {} \ No newline at end of file diff --git a/src/Api/Protos/google/protobuf/timestamp.proto b/src/Api/Protos/google/protobuf/timestamp.proto new file mode 100644 index 00000000..57a85311 --- /dev/null +++ b/src/Api/Protos/google/protobuf/timestamp.proto @@ -0,0 +1,138 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option cc_enable_arenas = true; +option go_package = "types"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "TimestampProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; + +// A Timestamp represents a point in time independent of any time zone or local +// calendar, encoded as a count of seconds and fractions of seconds at +// nanosecond resolution. The count is relative to an epoch at UTC midnight on +// January 1, 1970, in the proleptic Gregorian calendar which extends the +// Gregorian calendar backwards to year one. +// +// All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap +// second table is needed for interpretation, using a [24-hour linear +// smear](https://developers.google.com/time/smear). +// +// The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By +// restricting to that range, we ensure that we can convert to and from [RFC +// 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings. +// +// # Examples +// +// Example 1: Compute Timestamp from POSIX `time()`. +// +// Timestamp timestamp; +// timestamp.set_seconds(time(NULL)); +// timestamp.set_nanos(0); +// +// Example 2: Compute Timestamp from POSIX `gettimeofday()`. +// +// struct timeval tv; +// gettimeofday(&tv, NULL); +// +// Timestamp timestamp; +// timestamp.set_seconds(tv.tv_sec); +// timestamp.set_nanos(tv.tv_usec * 1000); +// +// Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`. +// +// FILETIME ft; +// GetSystemTimeAsFileTime(&ft); +// UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime; +// +// // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z +// // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z. +// Timestamp timestamp; +// timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL)); +// timestamp.set_nanos((INT32) ((ticks % 10000000) * 100)); +// +// Example 4: Compute Timestamp from Java `System.currentTimeMillis()`. +// +// long millis = System.currentTimeMillis(); +// +// Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000) +// .setNanos((int) ((millis % 1000) * 1000000)).build(); +// +// +// Example 5: Compute Timestamp from current time in Python. +// +// timestamp = Timestamp() +// timestamp.GetCurrentTime() +// +// # JSON Mapping +// +// In JSON format, the Timestamp type is encoded as a string in the +// [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the +// format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" +// where {year} is always expressed using four digits while {month}, {day}, +// {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional +// seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution), +// are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone +// is required. A proto3 JSON serializer should always use UTC (as indicated by +// "Z") when printing the Timestamp type and a proto3 JSON parser should be +// able to accept both UTC and other timezones (as indicated by an offset). +// +// For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past +// 01:30 UTC on January 15, 2017. +// +// In JavaScript, one can convert a Date object to this format using the +// standard +// [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString) +// method. In Python, a standard `datetime.datetime` object can be converted +// to this format using +// [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with +// the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use +// the Joda Time's [`ISODateTimeFormat.dateTime()`]( +// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime%2D%2D +// ) to obtain a formatter capable of generating timestamps in this format. +// +// +message Timestamp { + // Represents seconds of UTC time since Unix epoch + // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to + // 9999-12-31T23:59:59Z inclusive. + int64 seconds = 1; + + // Non-negative fractions of a second at nanosecond resolution. Negative + // second values with fractions must still have non-negative nanos values + // that count forward in time. Must be from 0 to 999,999,999 + // inclusive. + int32 nanos = 2; +} \ No newline at end of file diff --git a/src/Api/Protos/secret/compute/v1beta1/msg.proto b/src/Api/Protos/secret/compute/v1beta1/msg.proto index 417f7c2b..1a161c06 100644 --- a/src/Api/Protos/secret/compute/v1beta1/msg.proto +++ b/src/Api/Protos/secret/compute/v1beta1/msg.proto @@ -6,12 +6,20 @@ option go_package = "github.com/enigmampc/SecretNetwork/x/compute/internal/types import "gogoproto/gogo.proto"; import "cosmos/base/v1beta1/coin.proto"; -//import "x/compute/internal/types/types.proto"; +// Msg defines the wasm Msg service. +service Msg { + // 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); +} message MsgStoreCode { option (gogoproto.goproto_getters) = false; - + // sender is the canonical address of the sender bytes sender = 1 [(gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress"]; // WASMByteCode can be raw or gzip compressed bytes wasm_byte_code = 2 [(gogoproto.customname) = "WASMByteCode"]; @@ -19,99 +27,52 @@ message MsgStoreCode { string source = 3; // Builder is a valid docker image name with tag, optional string builder = 4; - // InstantiatePermission to apply on contract creation, optional -// AccessConfig InstantiatePermission = 5; +} + +// 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; + // sender is the canonical address of the sender bytes sender = 1 [(gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress"]; - // Admin is an optional address that can execute migrations -// bytes admin = 2 [(gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress"]; string callback_code_hash = 2; uint64 code_id = 3 [(gogoproto.customname) = "CodeID"]; string label = 4; bytes init_msg = 5; repeated cosmos.base.v1beta1.Coin init_funds = 6 [(gogoproto.nullable) = false, (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"]; } +// 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; + // 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"]; bytes msg = 3; string callback_code_hash = 4; repeated cosmos.base.v1beta1.Coin sent_funds = 5 [(gogoproto.nullable) = false, (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"]; } -// Todo: keeping this here for future replacing of bytes -> string -//syntax = "proto3"; -//package secret.compute.v1beta1; -// -//option go_package = "github.com/enigmampc/SecretNetwork/x/compute/internal/types"; -// -//import "gogoproto/gogo.proto"; -//import "cosmos/base/v1beta1/coin.proto"; -// -////import "x/compute/internal/types/types.proto"; -// -// -//message MsgStoreCode { -// option (gogoproto.goproto_getters) = false; -// -// // -// string sender = 1 [(gogoproto.moretags) = "yaml:\"sender\""]; -// // 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 [(gogoproto.moretags) = "yaml:\"source\""]; -// // Builder is a valid docker image name with tag, optional -// string builder = 4 [(gogoproto.moretags) = "yaml:\"builder\""]; -//} -// -//message MsgInstantiateContract { -// option (gogoproto.goproto_getters) = false; -// -// // -// string sender = 1 [(gogoproto.moretags) = "yaml:\"sender\""]; -// // -// string callback_code_hash = 2 [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"callback_code_hash\"" ]; -// // -// uint64 code_id = 3 [(gogoproto.customname) = "CodeID", (gogoproto.moretags) = "yaml:\"code_id\""]; -// // -// string label = 4 [(gogoproto.moretags) = "yaml:\"label\""]; -// // -// bytes init_msg = 5 [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"init_msg\"" ]; -// // -// repeated cosmos.base.v1beta1.Coin init_funds = 6 [ -// (gogoproto.nullable) = false, -// (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins", -// (gogoproto.moretags) = "yaml:\"init_funds\"" -// ]; -// // -// bytes callback_sig = 7 [(gogoproto.customname) = "CallbackSig", (gogoproto.moretags) = "yaml:\"callback_sig\""]; -//} -// -//message MsgExecuteContract { -// option (gogoproto.goproto_getters) = false; -// // -// string sender = 1 [(gogoproto.moretags) = "yaml:\"sender\""]; -// // -// string contract = 2 [(gogoproto.moretags) = "yaml:\"contract\""]; -// // -// bytes msg = 3 [(gogoproto.moretags) = "yaml:\"msg\""]; -// // -// string callback_code_hash = 4 [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"callback_code_hash\"" ]; -// // -// repeated cosmos.base.v1beta1.Coin sent_funds = 5 [ -// (gogoproto.nullable) = false, -// (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins", -// (gogoproto.moretags) = "yaml:\"sent_funds\"" -// ]; -// // -// bytes callback_sig = 6 [(gogoproto.customname) = "CallbackSig", (gogoproto.moretags) = "yaml:\"callback_sig\""]; -//} \ No newline at end of file +// MsgExecuteContractResponse returns execution result data. +message MsgExecuteContractResponse { + // Data contains base64-encoded bytes to returned from the contract + bytes data = 1; +} \ No newline at end of file diff --git a/src/Api/Protos/secret/compute/v1beta1/query.proto b/src/Api/Protos/secret/compute/v1beta1/query.proto index e5c874dc..15b87bd8 100644 --- a/src/Api/Protos/secret/compute/v1beta1/query.proto +++ b/src/Api/Protos/secret/compute/v1beta1/query.proto @@ -11,147 +11,122 @@ option go_package = "github.com/enigmampc/SecretNetwork/x/compute/internal/types option (gogoproto.goproto_getters_all) = false; option (gogoproto.equal_all) = true; - -// Query provides defines the gRPC querier service +// Query defines the gRPC querier service service Query { - // Query contract - rpc ContractInfo (QueryContractInfoRequest) returns (QueryContractInfoResponse) { - option (google.api.http).get = "/compute/v1beta1/contract/{address}"; + // Query contract info by address + rpc ContractInfo(QueryByContractAddressRequest) + returns (QueryContractInfoResponse) { + option (google.api.http).get = + "/compute/v1beta1/info/{contract_address}"; } - // Query contract - rpc ContractsByCode (QueryContractsByCodeRequest) returns (QueryContractsByCodeResponse) { - option (google.api.http).get = "/compute/v1beta1/code/{code_id}/contracts"; + // Query code info by id + rpc ContractsByCodeID(QueryByCodeIDRequest) + returns (QueryContractsByCodeIDResponse) { + option (google.api.http).get = "/compute/v1beta1/contracts/{code_id}"; } - // Query contract - rpc SmartContractState (QuerySmartContractStateRequest) returns (QuerySmartContractStateResponse) { - option (google.api.http).get = "/compute/v1beta1/contract/{address}/smart"; + // Query secret contract + rpc QuerySecretContract(QuerySecretContractRequest) + returns (QuerySecretContractResponse) { + option (google.api.http).get = + "/compute/v1beta1/query/{contract_address}"; } - // Query a specific contract code - rpc Code (QueryCodeRequest) returns (QueryCodeResponse) { + // 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/code"; + 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/{contract_address}"; + } + // Query code hash by code id + rpc CodeHashByCodeID(QueryByCodeIDRequest) returns (QueryCodeHashResponse) { + option (google.api.http).get = "/compute/v1beta1/code_hash/{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}"; } } -// QueryContractInfoRequest is the request type for the Query/ContractInfo RPC method -message QueryContractInfoRequest { - // address is the address of the contract to query - bytes address = 1 [(gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress"]; -} -// QueryContractInfoResponse is the response type for the Query/ContractInfo RPC method -message QueryContractInfoResponse { - // address is the address of the contract - bytes address = 1 [(gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress"]; - ContractInfo ContractInfo = 2 [(gogoproto.embed) = true, (gogoproto.jsontag) = ""]; -} - -message QueryContractHistoryRequest { - // address is the address of the contract to query - bytes address = 1 [(gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress"]; -} - -/* -message QueryContractHistoryResponse { - repeated ContractCodeHistoryEntry entries = 1 [(gogoproto.nullable) = false]; -} -*/ - -message QueryContractsByCodeRequest { - uint64 code_id = 1; // grpc-gateway_out does not support Go style CodID -} - -// ContractInfoWithAddress adds the address (key) to the ContractInfo representation -message ContractInfoWithAddress { - bytes address = 1 [(gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress"]; - ContractInfo ContractInfo = 2 [(gogoproto.embed) = true, (gogoproto.jsontag) = ""]; -} - -message QueryContractsByCodeResponse { - repeated ContractInfoWithAddress contract_infos = 1 [(gogoproto.nullable) = false]; -} - -/* -message QueryAllContractStateRequest { - // address is the address of the contract - bytes address = 1 [(gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress"]; -} - -message QueryAllContractStateResponse { - repeated Model models = 1 [(gogoproto.nullable) = false]; -} - -message QueryRawContractStateRequest { - // address is the address of the contract - bytes address = 1 [(gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress"]; - bytes query_data = 2; +message QuerySecretContractRequest { + // address is the bech32 human readable address of the contract + string contract_address = 1; + bytes query = 2; } -message QueryRawContractStateResponse { - bytes data = 1; -} -*/ +message QueryByLabelRequest { string label = 1; } -message QuerySmartContractStateRequest { - // address is the address of the contract - bytes address = 1 [(gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress"]; - bytes query_data = 2; +message QueryByContractAddressRequest { + // address is the bech32 human readable address of the contract + string contract_address = 1; } -message QueryContractAddressByLabelRequest { - string label = 1; -} +message QueryByCodeIDRequest { uint64 code_id = 1; } -message QueryContractKeyRequest { - // address is the address of the contract - bytes address = 1 [(gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress"]; -} +message QuerySecretContractResponse { bytes data = 1; } -message QueryContractHashRequest { - // address is the address of the contract - bytes address = 1 [(gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress"]; +// 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 ContractInfo = 2 + [ (gogoproto.embed) = true, (gogoproto.jsontag) = "" ]; } -message QuerySmartContractStateResponse { - bytes data = 1; +// 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 ContractInfo = 2 + [ (gogoproto.embed) = true, (gogoproto.jsontag) = "" ]; } -message QueryCodeRequest { - uint64 code_id = 1; // grpc-gateway_out does not support Go style CodID +message QueryContractsByCodeIDResponse { + repeated ContractInfoWithAddress contract_infos = 1 + [ (gogoproto.nullable) = false ]; } message CodeInfoResponse { - uint64 code_id = 1 [(gogoproto.customname) = "CodeID", (gogoproto.jsontag) = "id"]; // id for legacy support - bytes creator = 2 [(gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress"]; - bytes data_hash = 3 [(gogoproto.casttype) = "github.com/tendermint/tendermint/libs/bytes.HexBytes"]; + 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 data = 2 [(gogoproto.jsontag) = "data"]; + CodeInfoResponse code_info = 1 + [ (gogoproto.embed) = true, (gogoproto.jsontag) = "" ]; + bytes wasm = 2; } message QueryCodesResponse { - repeated CodeInfoResponse code_infos = 1 [(gogoproto.nullable) = false]; + repeated CodeInfoResponse code_infos = 1 [ (gogoproto.nullable) = false ]; } -message QueryContractAddressByLabelResponse { - // address is the address of the contract - bytes address = 1 [(gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress"]; +message QueryContractAddressResponse { + // address is the bech32 human readable address of the contract + string contract_address = 1; } -message QueryContractKeyResponse { - // address is the address of the contract - bytes key = 1 [(gogoproto.casttype) = "github.com/tendermint/tendermint/libs/bytes.HexBytes"]; -} +message QueryContractLabelResponse { string label = 1; } -message QueryContractHashResponse { - bytes code_hash = 1 [(gogoproto.casttype) = "github.com/tendermint/tendermint/libs/bytes.HexBytes"]; -} +message QueryCodeHashResponse { string code_hash = 1; } // DecryptedAnswer is a struct that represents a decrypted tx-query message DecryptedAnswer { @@ -161,7 +136,14 @@ message DecryptedAnswer { string input = 2; string output_data = 3; string output_data_as_string = 4; - repeated cosmos.base.abci.v1beta1.StringEvent output_logs = 5 [(gogoproto.nullable) = false]; - bytes output_error = 6 [(gogoproto.casttype) = "encoding/json.RawMessage"]; - string plaintext_error = 7; } + +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; +} \ No newline at end of file diff --git a/src/Api/Protos/secret/compute/v1beta1/types.proto b/src/Api/Protos/secret/compute/v1beta1/types.proto index 216dff18..f706de46 100644 --- a/src/Api/Protos/secret/compute/v1beta1/types.proto +++ b/src/Api/Protos/secret/compute/v1beta1/types.proto @@ -21,30 +21,12 @@ message AccessTypeParam { AccessType value = 1 [(gogoproto.moretags) = "yaml:\"value\""]; } -/* -message AccessConfig { - option (gogoproto.goproto_stringer) = true; - AccessType permission = 1 [(gogoproto.moretags) = "yaml:\"permission\""]; - bytes address = 2 [(gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress", (gogoproto.moretags) = "yaml:\"address\""]; -} - */ - -/* -// Params defines the set of wasm parameters. -message Params { - option (gogoproto.goproto_stringer) = false; - AccessConfig code_upload_access = 1 [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"code_upload_access\""]; - AccessType instantiate_default_permission = 2 [(gogoproto.moretags) = "yaml:\"instantiate_default_permission\""]; -} -*/ - // 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; -// AccessConfig instantiate_config = 5 [(gogoproto.nullable) = false]; } message ContractCustomInfo { @@ -56,43 +38,12 @@ message ContractCustomInfo { message ContractInfo { uint64 code_id = 1 [(gogoproto.customname) = "CodeID"]; bytes creator = 2 [(gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress"]; -// bytes admin = 3 [(gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress"]; string label = 4; // never show this in query results, just use for sorting // (Note: when using json tag "-" amino refused to serialize it...) AbsoluteTxPosition created = 5; - // bytes init_msg = 5 [(gogoproto.casttype) = "encoding/json.RawMessage"]; - // - // AbsoluteTxPosition last_updated = 7; - // uint64 previous_code_id = 8 [(gogoproto.customname) = "PreviousCodeID"]; -} - -/* -enum ContractCodeHistoryOperationType { - option (gogoproto.goproto_enum_prefix) = false; - Undefined = 0; - Init = 1 [(gogoproto.enumvalue_customname) = "ContractCodeHistoryTypeInit"]; - Migrate = 2 [(gogoproto.enumvalue_customname) = "ContractCodeHistoryTypeMigrate"]; - Genesis = 3 [(gogoproto.enumvalue_customname) = "ContractCodeHistoryTypeGenesis"]; -} -*/ - -/* -message ContractHistory { - repeated ContractCodeHistoryEntry code_history_entries = 1 [(gogoproto.nullable) = false]; -} -*/ - -/* -// ContractCodeHistoryEntry stores code updates to a contract. -message ContractCodeHistoryEntry { - ContractCodeHistoryOperationType operation = 1; - uint64 code_id = 2 [(gogoproto.customname) = "CodeID"]; - AbsoluteTxPosition updated = 3; - bytes msg = 4 [(gogoproto.casttype) = "encoding/json.RawMessage"]; - + string ibc_port_id = 6 [ (gogoproto.customname) = "IBCPortID" ]; } -*/ // AbsoluteTxPosition can be used to sort contracts message AbsoluteTxPosition { diff --git a/src/Query/ComputeQueryClient.cs b/src/Query/ComputeQueryClient.cs index 95403c27..23f43b4b 100644 --- a/src/Query/ComputeQueryClient.cs +++ b/src/Query/ComputeQueryClient.cs @@ -65,12 +65,12 @@ public async Task> QueryContract(string contract var nonce = new ArraySegment(encryptedQuery, 0, 32).ToArray(); try { - var request = new Secret.Compute.V1Beta1.QuerySmartContractStateRequest() + var request = new Secret.Compute.V1Beta1.QuerySecretContractRequest() { - Address = AddressToBytes(contractAddress).GetByteStringFromBase64(), - QueryData = encryptedQuery.GetByteStringFromBase64() + ContractAddress = contractAddress, + Query = encryptedQuery.GetByteStringFromBase64() }; - var queryResponse = await client.SmartContractStateAsync(request); + var queryResponse = await client.QuerySecretContractAsync(request); if (queryResponse != null) { @@ -196,7 +196,7 @@ public async Task GetCodeHashByCodeId(ulong codeId) /// public async Task Code(ulong codeId) { - var request = new Secret.Compute.V1Beta1.QueryCodeRequest() + var request = new Secret.Compute.V1Beta1.QueryByCodeIDRequest { CodeId = codeId }; @@ -216,9 +216,9 @@ public async Task Code(ulong codeId) /// public async Task ContractInfo(string contractAddress) { - var request = new Secret.Compute.V1Beta1.QueryContractInfoRequest() + var request = new Secret.Compute.V1Beta1.QueryByContractAddressRequest { - Address = ByteString.FromBase64(Convert.ToBase64String(AddressToBytes(contractAddress))) + ContractAddress = contractAddress }; var result = await client.ContractInfoAsync(request); if (result?.ContractInfo != null) @@ -237,11 +237,11 @@ public async Task ContractInfo(string contractAddress) /// public async Task> ContractsByCode(ulong codeId) { - var request = new Secret.Compute.V1Beta1.QueryContractsByCodeRequest() + var request = new Secret.Compute.V1Beta1.QueryByCodeIDRequest { CodeId = codeId }; - var result = await client.ContractsByCodeAsync(request); + var result = await client.ContractsByCodeIDAsync(request); if ((result?.ContractInfos?.Any()).GetValueOrDefault()) { var resultList = new List(); @@ -324,7 +324,7 @@ private SecretContractInfo ParseContractInfo(Secret.Compute.V1Beta1.ContractInfo if (contractInfo?.ContractInfo != null) { var parsedInfo = ParseContractInfo(contractInfo.ContractInfo); - parsedInfo.Address = contractInfo.Address.ToStringUtf8(); + parsedInfo.Address = contractInfo.ContractAddress; return parsedInfo; } return null; @@ -342,8 +342,8 @@ private SecretCodeInfo ParseCodeInfo(Secret.Compute.V1Beta1.CodeInfoResponse cod return new SecretCodeInfo() { CodeId = codeInfo.CodeId, - CreatorAddress = SecretNetworkClient.BytesToAddress(codeInfo.Creator.ToByteArray()), - CodeHash = Convert.ToHexString(codeInfo.DataHash.ToByteArray()).ToLower(), + CreatorAddress = codeInfo.Creator, + CodeHash = codeInfo.CodeHash, Builder = codeInfo.Builder, Source = codeInfo.Source }; diff --git a/src/SecretNET.csproj b/src/SecretNET.csproj index 25999752..69064853 100644 --- a/src/SecretNET.csproj +++ b/src/SecretNET.csproj @@ -21,7 +21,7 @@ True True Secret.NET (port of the secret.js Client) is an .NET SDK for writing applications that interact with the Secret Network blockchain. - 0.2.2 + 0.3.0 NuGet_README.md https://github.com/0xxCodemonkey/SecretNET blockchain;privacy @@ -31,6 +31,7 @@ Secret.NET (port of the secret.js Client) is an .NET SDK for writing applications that interact with the Secret Network blockchain. Codemonkey Codemonkey + Shockwave Delta Update From df53fd98f26ecbe53f96212c27878ff2abea7872 Mon Sep 17 00:00:00 2001 From: 0xxCodemonkey Date: Sun, 18 Sep 2022 05:58:55 +0200 Subject: [PATCH 02/31] CosmWasm 1.0, adaptation to secretjs changes --- src/Api/ComputeMsgToNonce.cs | 6 +- src/Api/CosmosSdkException.cs | 14 +- src/Api/SecretUtils.cs | 12 +- src/Api/StdFee.cs | 22 +- src/Common/SecretEncryptionUtil.cs | 8 +- src/Crypto/Miscreant/Siv.cs | 7 +- src/ISecretNetworkClient.cs | 6 +- src/Query/ComputeQueryClient.cs | 92 ++++---- src/Query/SecretContractInfo.cs | 6 + src/SecretNetworkClient.cs | 33 +-- src/Tx/IBC_Client.Msg.cs | 2 +- src/Tx/MsgDecoderRegistry.cs | 109 ++++++++++ src/Tx/MsgTypeNames.cs | 1 - src/Tx/SecretTx.cs | 109 ++++++++-- src/Tx/TxClient.cs | 329 ++++++++++++++++++++++------- src/Tx/TxOptions.cs | 6 + 16 files changed, 586 insertions(+), 176 deletions(-) create mode 100644 src/Tx/MsgDecoderRegistry.cs diff --git a/src/Api/ComputeMsgToNonce.cs b/src/Api/ComputeMsgToNonce.cs index 590b05c8..ddc6186c 100644 --- a/src/Api/ComputeMsgToNonce.cs +++ b/src/Api/ComputeMsgToNonce.cs @@ -1,9 +1,11 @@ -namespace SecretNET.Api; +using System.Collections.Concurrent; + +namespace SecretNET.Api; /// /// Dictionary which assigns messages index to nonce. /// -public class ComputeMsgToNonce : Dictionary +public class ComputeMsgToNonce : ConcurrentDictionary { } diff --git a/src/Api/CosmosSdkException.cs b/src/Api/CosmosSdkException.cs index 8fd7ba45..2b7a4d6c 100644 --- a/src/Api/CosmosSdkException.cs +++ b/src/Api/CosmosSdkException.cs @@ -1,4 +1,6 @@ -namespace SecretNET.Api; +using Cosmos.Base.Abci.V1Beta1; + +namespace SecretNET.Api; /// /// Class CosmosSdkException. @@ -23,7 +25,7 @@ public class CosmosSdkException : Exception /// Gets the get tx response. /// /// The get tx response. - public GetTxResponse GetTxResponse { get; private set; } + public TxResponse TxResponse { get; private set; } /// /// Gets the raw log. @@ -36,9 +38,9 @@ public string RawLog { return BroadcastTxResponse?.TxResponse?.RawLog; } - if (GetTxResponse?.TxResponse?.RawLog != null) + if (TxResponse?.RawLog != null) { - return GetTxResponse?.TxResponse?.RawLog; + return TxResponse?.RawLog; } return null; } @@ -60,10 +62,10 @@ public CosmosSdkException(CosmosSdkErrorEnum cosmosSdkErrorEnum, BroadcastTxResp /// /// The cosmos SDK error enum. /// The response. - public CosmosSdkException(CosmosSdkErrorEnum cosmosSdkErrorEnum, GetTxResponse response) : base(SetErrorMessage(cosmosSdkErrorEnum)) + public CosmosSdkException(CosmosSdkErrorEnum cosmosSdkErrorEnum, TxResponse response) : base(SetErrorMessage(cosmosSdkErrorEnum)) { CosmosSdkErrorEnum = cosmosSdkErrorEnum; - GetTxResponse = response; + TxResponse = response; } // https://github.com/cosmos/cosmos-sdk/blob/main/types/errors/errors.go diff --git a/src/Api/SecretUtils.cs b/src/Api/SecretUtils.cs index c57df323..b5ac6fa7 100644 --- a/src/Api/SecretUtils.cs +++ b/src/Api/SecretUtils.cs @@ -79,11 +79,21 @@ public static bool IsProtoType(this string typeUrl, string matchType) { if (!String.IsNullOrEmpty(typeUrl) && !String.IsNullOrEmpty(matchType)) { - return typeUrl.TrimStart('/').Equals(matchType, StringComparison.InvariantCultureIgnoreCase); + var cleanedType = CleanUpTypeurl(typeUrl); + return cleanedType.Equals(matchType, StringComparison.InvariantCultureIgnoreCase); } return false; } + public static string CleanUpTypeurl(this string typeUrl) + { + if (!String.IsNullOrEmpty(typeUrl)) + { + return Regex.Match(typeUrl, "[^/]([^/]*)$").Groups[0].Value; + } + return typeUrl; + } + public static bool IsProtoType(this IMessage msg, string matchType) { if (!String.IsNullOrEmpty(msg?.Descriptor?.FullName)) diff --git a/src/Api/StdFee.cs b/src/Api/StdFee.cs index d55039a3..cce7158a 100644 --- a/src/Api/StdFee.cs +++ b/src/Api/StdFee.cs @@ -13,14 +13,18 @@ public class StdFee [JsonProperty("gas")] public string Gas { get; set; } - public StdFee(Cosmos.Base.V1Beta1.Coin coin, string gas): this(new[] { coin }, gas) + [JsonProperty("granter")] + public string FeeGranter { get; set; } + + public StdFee(Cosmos.Base.V1Beta1.Coin coin, string gas, string feeGranter = null) : this(new[] { coin }, gas, feeGranter) { } - public StdFee(Cosmos.Base.V1Beta1.Coin[] coins, string gas) + public StdFee(Cosmos.Base.V1Beta1.Coin[] coins, string gas, string feeGranter = null) { Amount = coins; Gas = gas; + FeeGranter = feeGranter; } public static StdFee GetDefault(){ @@ -34,7 +38,7 @@ public static StdFee FromTxOptions(TxOptions txOptions) Denom = txOptions.FeeDenom }; - return new StdFee(new Cosmos.Base.V1Beta1.Coin[] { coin }, txOptions.GasLimit.ToString()); + return new StdFee(new Cosmos.Base.V1Beta1.Coin[] { coin }, txOptions.GasLimit.ToString(), txOptions.FeeGranter); } public StdFeeAmino ConvertToStdFeeAmino() @@ -56,20 +60,24 @@ public class StdFeeAmino [JsonProperty("gas")] public string Gas { get; set; } - public StdFeeAmino(Tx.Coin coin, string gas) : this(new[] { coin }, gas) + [JsonProperty("granter")] + public string FeeGranter { get; set; } + + public StdFeeAmino(Tx.Coin coin, string gas, string feeGranter = null) : this(new[] { coin }, gas, feeGranter) { } - public StdFeeAmino(Tx.Coin[] coins, string gas) + public StdFeeAmino(Tx.Coin[] coins, string gas, string feeGranter = null) { Amount = coins; Gas = gas; + FeeGranter = feeGranter; } public StdFee ConvertToStdFee() { var coins = Amount.Select(c => new Cosmos.Base.V1Beta1.Coin() { Amount = c.Amount, Denom = c.Denom }).ToArray(); - var result = new StdFee(coins, Gas); + var result = new StdFee(coins, Gas, FeeGranter); return result; } @@ -86,7 +94,7 @@ public static StdFeeAmino FromTxOptions(TxOptions txOptions) Denom = txOptions.FeeDenom }; - return new StdFeeAmino(new Tx.Coin[] { coin }, txOptions.GasLimit.ToString()); + return new StdFeeAmino(new Tx.Coin[] { coin }, txOptions.GasLimit.ToString(), txOptions.FeeGranter); } } diff --git a/src/Common/SecretEncryptionUtil.cs b/src/Common/SecretEncryptionUtil.cs index 9425f795..62241bf9 100644 --- a/src/Common/SecretEncryptionUtil.cs +++ b/src/Common/SecretEncryptionUtil.cs @@ -67,7 +67,13 @@ public SecretEncryptionUtils(string chainId, IRegistrationQueryClient registrati /// System.Byte[]. public async Task Decrypt(byte[] ciphertext, byte[] nonce) { + if ((ciphertext?.Length).GetValueOrDefault() == 0) + { + return new byte[0]; + } + var txEncryptionKey = await GetTxEncryptionKey(nonce); + var siv = Siv.ImportKey(txEncryptionKey); var plaintext = siv.Open(ciphertext); @@ -83,8 +89,8 @@ public async Task Decrypt(byte[] ciphertext, byte[] nonce) public async Task Encrypt(string contractCodeHash, object contractMsg) { var nonce = GenerateNewSeed(); - var txEncryptionKey = await GetTxEncryptionKey(nonce); + var siv = Siv.ImportKey(txEncryptionKey); var contractMsgAsString = (contractMsg is string) ? contractMsg : JsonConvert.SerializeObject(contractMsg); diff --git a/src/Crypto/Miscreant/Siv.cs b/src/Crypto/Miscreant/Siv.cs index 0fbe5787..649ed239 100644 --- a/src/Crypto/Miscreant/Siv.cs +++ b/src/Crypto/Miscreant/Siv.cs @@ -63,7 +63,7 @@ private Siv(IMACLike mac, ICTRLike ctr) /// System.Byte[]. /// /// - public byte[] Seal(byte[] plaintext, byte[][]? associatedData = null) + public byte[] Seal(byte[] plaintext, byte[][] associatedData = null) { associatedData = new byte[][] { new byte[0] }; if (associatedData.Length > MAX_ASSOCIATED_DATA) @@ -89,9 +89,12 @@ public byte[] Seal(byte[] plaintext, byte[][]? associatedData = null) /// /// Decrypt and authenticate data using AES-SIV /// - /// The plaintext. + /// The sealed text. /// The associated data. /// System.Byte[]. + /// AES-SIV: too many associated data items + /// AES-SIV: ciphertext is truncated + /// AES-SIV: ciphertext verification failure! /// public byte[] Open(byte[] sealedText, byte[][]? associatedData = null) { diff --git a/src/ISecretNetworkClient.cs b/src/ISecretNetworkClient.cs index 8effb472..ae44cff9 100644 --- a/src/ISecretNetworkClient.cs +++ b/src/ISecretNetworkClient.cs @@ -45,7 +45,7 @@ public interface ISecretNetworkClient /// The messages. /// The tx options. /// Task<System.ValueTuple<System.Byte[], ComputeMsgToNonce>>. - public Task<(byte[] TxBytes, ComputeMsgToNonce Nonces)> PrepareAndSign(Tx.MsgBase[] messages, TxOptions? txOptions = null); + public Task PrepareAndSign(Tx.MsgBase[] messages, TxOptions? txOptions = null); /// /// Signs the specified messages. @@ -55,7 +55,7 @@ public interface ISecretNetworkClient /// The memo. /// The explicit signer data. /// Task<System.ValueTuple<TxRaw, ComputeMsgToNonce>>. - public Task<(TxRaw TxRaw, ComputeMsgToNonce Nonces)> Sign(Tx.MsgBase[] messages, StdFee fee, string? memo = null, SignerData? explicitSignerData = null); + public Task Sign(Tx.MsgBase[] messages, StdFee fee, string? memo = null, SignerData? explicitSignerData = null); /// /// Signs the transaction (direct). @@ -65,6 +65,6 @@ public interface ISecretNetworkClient /// The signer data. /// The memo. /// Task<System.ValueTuple<TxRaw, ComputeMsgToNonce>>. - public Task<(TxRaw TxRaw, ComputeMsgToNonce Nonces)> SignDirect(Tx.MsgBase[] messages, StdFee fee, SignerData signerData, string? memo = null); + public Task SignDirect(Tx.MsgBase[] messages, StdFee fee, SignerData signerData, string? memo = null); } diff --git a/src/Query/ComputeQueryClient.cs b/src/Query/ComputeQueryClient.cs index 23f43b4b..e5c5aeae 100644 --- a/src/Query/ComputeQueryClient.cs +++ b/src/Query/ComputeQueryClient.cs @@ -43,15 +43,16 @@ private Secret.Compute.V1Beta1.Query.QueryClient client /// Query a Secret Contract /// /// - /// - /// + /// The contract address. + /// The query MSG. /// codehash is optional but makes the first call way faster (after that the hash is cached in the client) - /// - public async Task> QueryContract(string contractAddress, object queryMsg, string codeHash = null) where R : class + /// The metadata. + /// SecretQueryContractResult<R>. + public async Task> QueryContract(string contractAddress, object queryMsg, string codeHash = null, Metadata metadata = null) where R : class { if (string.IsNullOrEmpty(codeHash)) { - codeHash = await GetCodeHash(contractAddress); + codeHash = await GetCodeHash(contractAddress, metadata); } SecretQueryContractResult result = null; @@ -70,7 +71,7 @@ public async Task> QueryContract(string contract ContractAddress = contractAddress, Query = encryptedQuery.GetByteStringFromBase64() }; - var queryResponse = await client.QuerySecretContractAsync(request); + var queryResponse = await client.QuerySecretContractAsync(request, metadata); if (queryResponse != null) { @@ -134,9 +135,10 @@ public async Task> QueryContract(string contract /// /// Get codeHash of a Secret Contract /// - /// - /// - public async Task GetCodeHash(string contractAddress) + /// The contract address. + /// The metadata. + /// System.String. + public async Task GetCodeHash(string contractAddress, Metadata metadata = null) { // Cache => via Provider Hashes je chainId speichern if (_codeHashCache.ContainsKey(contractAddress)) @@ -144,18 +146,17 @@ public async Task GetCodeHash(string contractAddress) return _codeHashCache[contractAddress]; } - string codeHash = null; - var codeInfoResult = await ContractInfo(contractAddress); + var codeInfoResult = await ContractInfo(contractAddress, metadata); if (codeInfoResult != null && codeInfoResult.CodeId > 0) { - var code = await Code(codeInfoResult.CodeId); - if (code != null && !String.IsNullOrEmpty(code.CodeHash)) + var code = await Code(codeInfoResult.CodeId, metadata); + if (code.CodeInfo != null && !String.IsNullOrEmpty(code.CodeInfo?.CodeHash)) { if (!_codeHashCache.ContainsKey(contractAddress)) { - _codeHashCache.TryAdd(contractAddress, code.CodeHash); + _codeHashCache.TryAdd(contractAddress, code.CodeInfo?.CodeHash); } - return code.CodeHash; + return code.CodeInfo?.CodeHash; } } @@ -165,9 +166,10 @@ public async Task GetCodeHash(string contractAddress) /// /// Get codeHash from a code id /// - /// - /// - public async Task GetCodeHashByCodeId(ulong codeId) + /// The code identifier. + /// The metadata. + /// System.String. + public async Task GetCodeHashByCodeId(ulong codeId, Metadata metadata = null) { // Cache => via Provider Hashes je chainId speichern if (_codeHashCacheByCodeId.ContainsKey(codeId)) @@ -175,15 +177,14 @@ public async Task GetCodeHashByCodeId(ulong codeId) return _codeHashCacheByCodeId[codeId]; } - string codeHash = null; - var code = await Code(codeId); - if (code != null && !String.IsNullOrEmpty(code.CodeHash)) + var code = await Code(codeId, metadata); + if (code.CodeInfo != null && !String.IsNullOrEmpty(code.CodeInfo.CodeHash)) { if (!_codeHashCacheByCodeId.ContainsKey(codeId)) { - _codeHashCacheByCodeId.TryAdd(codeId, code.CodeHash); + _codeHashCacheByCodeId.TryAdd(codeId, code.CodeInfo.CodeHash); } - return code.CodeHash; + return code.CodeInfo.CodeHash; } return null; @@ -192,35 +193,37 @@ public async Task GetCodeHashByCodeId(ulong codeId) /// /// Get WASM bytecode and metadata for a code id /// - /// - /// - public async Task Code(ulong codeId) + /// The code identifier. + /// The metadata. + /// SecretCodeInfo. + public async Task<(SecretCodeInfo CodeInfo, byte[] WasmBytes)> Code(ulong codeId, Metadata metadata = null) { var request = new Secret.Compute.V1Beta1.QueryByCodeIDRequest { CodeId = codeId }; - var result = await client.CodeAsync(request); + var result = await client.CodeAsync(request, metadata); if (result?.CodeInfo != null) { var parsedInfo = ParseCodeInfo(result.CodeInfo); - return parsedInfo; + return (parsedInfo, result.Wasm.ToByteArray()); } - return null; + return (null, null); } /// /// Get metadata of a Secret Contract /// - /// - /// - public async Task ContractInfo(string contractAddress) + /// The contract address. + /// The metadata. + /// SecretContractInfo. + public async Task ContractInfo(string contractAddress, Metadata metadata = null) { var request = new Secret.Compute.V1Beta1.QueryByContractAddressRequest { ContractAddress = contractAddress }; - var result = await client.ContractInfoAsync(request); + var result = await client.ContractInfoAsync(request, metadata); if (result?.ContractInfo != null) { var parsedInfo = ParseContractInfo(result.ContractInfo); @@ -233,15 +236,16 @@ public async Task ContractInfo(string contractAddress) /// /// Get all contracts that were instantiated from a code id. /// - /// - /// - public async Task> ContractsByCode(ulong codeId) + /// The code identifier. + /// The metadata. + /// List<SecretContractInfo>. + public async Task> ContractsByCode(ulong codeId, Metadata metadata = null) { var request = new Secret.Compute.V1Beta1.QueryByCodeIDRequest { CodeId = codeId }; - var result = await client.ContractsByCodeIDAsync(request); + var result = await client.ContractsByCodeIDAsync(request, metadata); if ((result?.ContractInfos?.Any()).GetValueOrDefault()) { var resultList = new List(); @@ -261,10 +265,11 @@ public async Task> ContractsByCode(ulong codeId) /// /// Query all codes on chain. /// - /// - public async Task> Codes() + /// The metadata. + /// List<SecretCodeInfo>. + public async Task> Codes(Metadata metadata = null) { - var result = await client.CodesAsync(new Empty()); + var result = await client.CodesAsync(new Empty(), metadata); if ((result?.CodeInfos?.Any()).GetValueOrDefault()) { @@ -299,7 +304,7 @@ private SecretContractInfo ParseContractInfo(Secret.Compute.V1Beta1.ContractInfo CodeId = contractInfo.CodeId, CreatorAddress = SecretNetworkClient.BytesToAddress(contractInfo.Creator.ToByteArray()), Label = contractInfo.Label, - + IbcPortId = contractInfo.IbcPortId }; if (contractInfo.Created != null) { @@ -339,6 +344,11 @@ private SecretCodeInfo ParseCodeInfo(Secret.Compute.V1Beta1.CodeInfoResponse cod { if (codeInfo != null) { + if (!_codeHashCacheByCodeId.ContainsKey(codeInfo.CodeId)) + { + _codeHashCacheByCodeId.TryAdd(codeInfo.CodeId, codeInfo.CodeHash); + } + return new SecretCodeInfo() { CodeId = codeInfo.CodeId, diff --git a/src/Query/SecretContractInfo.cs b/src/Query/SecretContractInfo.cs index 2eaeb38e..0c861b8d 100644 --- a/src/Query/SecretContractInfo.cs +++ b/src/Query/SecretContractInfo.cs @@ -24,6 +24,12 @@ public class SecretContractInfo /// public AbsoluteTxPosition? Created { get; set; } + /// + /// Gets or sets the ibc port identifier. + /// + /// The ibc port identifier. + public string IbcPortId { get; set; } + } public class AbsoluteTxPosition diff --git a/src/SecretNetworkClient.cs b/src/SecretNetworkClient.cs index c71ec234..80151000 100644 --- a/src/SecretNetworkClient.cs +++ b/src/SecretNetworkClient.cs @@ -166,14 +166,14 @@ public AccessControl.PermitSigner Permit /// The messages. /// The tx options. /// System.ValueTuple<System.Byte[], ComputeMsgToNonce>. - public async Task<(byte[] TxBytes, ComputeMsgToNonce Nonces)> PrepareAndSign(MsgBase[] messages, TxOptions? txOptions = null) + public async Task PrepareAndSign(MsgBase[] messages, TxOptions? txOptions = null) { txOptions = txOptions ?? new TxOptions(); var signResult = await Sign(messages, StdFee.FromTxOptions(txOptions), txOptions.Memo, txOptions.ExplicitSignerData); - var txBytes = signResult.TxRaw.Encode(); + var txBytes = signResult.Encode(); - return (txBytes, signResult.Nonces); + return txBytes; } /// @@ -184,7 +184,7 @@ public AccessControl.PermitSigner Permit /// The memo. /// The explicit signer data. /// System.ValueTuple<TxRaw, ComputeMsgToNonce>. - public async Task<(TxRaw TxRaw, ComputeMsgToNonce Nonces)> Sign(MsgBase[] messages, StdFee fee, string? memo = null, SignerData? explicitSignerData = null) + public async Task Sign(MsgBase[] messages, StdFee fee, string? memo = null, SignerData? explicitSignerData = null) { if (Wallet == null) { @@ -229,7 +229,8 @@ public AccessControl.PermitSigner Permit /// The signer data. /// The memo. /// System.ValueTuple<TxRaw, ComputeMsgToNonce>. - public async Task<(TxRaw TxRaw, ComputeMsgToNonce Nonces)> SignDirect(MsgBase[] messages, StdFee fee, SignerData signerData, string? memo = null) + /// Failed to retrieve account from signer + public async Task SignDirect(MsgBase[] messages, StdFee fee, SignerData signerData, string? memo = null) { if (Wallet == null) { @@ -244,7 +245,7 @@ public AccessControl.PermitSigner Permit var msg = messages[i]; await PopulateCodeHash(msg); var protoMsg = await msg.ToProto(EncryptionUtils); - encryptionNonces.Add(i, ExtractNonce(protoMsg)); + encryptionNonces.TryAdd(i, ExtractNonce(protoMsg)); var any = Any.Pack(protoMsg,""); @@ -253,7 +254,7 @@ public AccessControl.PermitSigner Permit var txBodyBytes = txBody.Encode(); var pubKey = EncodePubkey(EncodeSecp256k1Pubkey(Wallet.PublicKey)); - var authInfoBytes = MakeAuthInfoBytes(pubKey, signerData.Sequence, fee.Amount, ulong.Parse(fee.Gas), SignMode.Direct); + var authInfoBytes = MakeAuthInfoBytes(pubKey, signerData.Sequence, fee.Amount, ulong.Parse(fee.Gas), SignMode.Direct, fee.FeeGranter); var signDoc = MakeSignDocProto(txBodyBytes, authInfoBytes, ChainId, signerData.AccountNumber); var walletSign = await Wallet.SignDirect(signDoc, Wallet.Address); @@ -262,7 +263,7 @@ public AccessControl.PermitSigner Permit txRaw.AuthInfoBytes = signDoc.AuthInfoBytes; txRaw.Signatures.Add(ByteString.FromBase64(walletSign.Signature)); - return (txRaw, encryptionNonces); + return txRaw; } /// @@ -273,7 +274,8 @@ public AccessControl.PermitSigner Permit /// The signer data. /// The memo. /// System.ValueTuple<TxRaw, ComputeMsgToNonce>. - public async Task<(TxRaw TxRaw, ComputeMsgToNonce Nonces)> SignAmino(MsgBase[] messages, StdFee fee, SignerData signerData, string? memo = null) + /// Failed to retrieve account from signer + public async Task SignAmino(MsgBase[] messages, StdFee fee, SignerData signerData, string? memo = null) { if (Wallet == null) { @@ -297,7 +299,7 @@ public AccessControl.PermitSigner Permit var msg = messages[i]; await PopulateCodeHash(msg); var protoMsg = await msg.ToProto(EncryptionUtils); - encryptionNonces.Add(i, ExtractNonce(protoMsg)); + encryptionNonces.TryAdd(i, ExtractNonce(protoMsg)); var any = Any.Pack(protoMsg, ""); @@ -313,7 +315,7 @@ public AccessControl.PermitSigner Permit txRaw.AuthInfoBytes = ByteString.FromBase64(Convert.ToBase64String(authInfoBytes)); txRaw.Signatures.Add(ByteString.FromBase64(walletSign.Signature)); - return (txRaw, encryptionNonces); + return txRaw; } // static @@ -387,7 +389,7 @@ internal static StdSignDoc MakeSignDocAmino(AminoMsg[] msgs, StdFee fee, string } - internal static byte[] MakeAuthInfoBytes(Any encodedPubKey, ulong sequence, Cosmos.Base.V1Beta1.Coin[] feeAmount, ulong gasLimit, SignMode signMode = SignMode.Direct) + internal static byte[] MakeAuthInfoBytes(Any encodedPubKey, ulong sequence, Cosmos.Base.V1Beta1.Coin[] feeAmount, ulong gasLimit, SignMode signMode = SignMode.Direct, string feeGranter = null) { var signers = new Dictionary(); signers.Add(encodedPubKey, sequence); @@ -395,7 +397,7 @@ internal static byte[] MakeAuthInfoBytes(Any encodedPubKey, ulong sequence, Cosm return MakeAuthInfoBytes(signers, feeAmount, gasLimit, signMode); } - internal static byte[] MakeAuthInfoBytes(Dictionary signers, Cosmos.Base.V1Beta1.Coin[] feeAmount, ulong gasLimit, SignMode signMode = SignMode.Direct) + internal static byte[] MakeAuthInfoBytes(Dictionary signers, Cosmos.Base.V1Beta1.Coin[] feeAmount, ulong gasLimit, SignMode signMode = SignMode.Direct, string feeGranter = null) { var authInfo = new AuthInfo(); var signerInfos = MakeSignerInfos(signers, signMode); @@ -405,6 +407,10 @@ internal static byte[] MakeAuthInfoBytes(Dictionary signers, Cosmos. GasLimit = gasLimit }; authInfo.Fee.Amount.Add(feeAmount); + if (!String.IsNullOrWhiteSpace(feeGranter)) + { + authInfo.Fee.Granter = feeGranter; + } return authInfo.Encode(); } @@ -470,6 +476,7 @@ private byte[] ExtractNonce(IMessage protoMsg) { var msg = (Secret.Compute.V1Beta1.MsgInstantiateContract)protoMsg; var slice = new ArraySegment(msg.InitMsg.Span.ToArray(), 0, 32).ToArray(); + Console.WriteLine("ExtractNonce nonce: " + slice.ToHexString()); return slice; } return new byte[0]; diff --git a/src/Tx/IBC_Client.Msg.cs b/src/Tx/IBC_Client.Msg.cs index f0142384..f3af3b44 100644 --- a/src/Tx/IBC_Client.Msg.cs +++ b/src/Tx/IBC_Client.Msg.cs @@ -40,7 +40,7 @@ public override Task ToAmino(SecretEncryptionUtils utils) /// public class MsgCreateClient : MsgBase { - public override string MsgType { get; } = MsgGrantAuthorization.MsgTimeoutOnClose; + public override string MsgType { get; } = MsgGrantAuthorization.MsgCreateClient; public override Task ToProto(SecretEncryptionUtils utils) { diff --git a/src/Tx/MsgDecoderRegistry.cs b/src/Tx/MsgDecoderRegistry.cs new file mode 100644 index 00000000..be1c4d64 --- /dev/null +++ b/src/Tx/MsgDecoderRegistry.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SecretNET.Tx; + +internal static class MsgDecoderRegistry +{ + internal static readonly ConcurrentDictionary> Registry = new ConcurrentDictionary>(); + + static MsgDecoderRegistry() + { + // Cosmos.Authz.V1Beta1 + Registry.TryAdd(MsgGrantAuthorization.MsgGrant.ToLower(), (any) => any.Unpack()); + Registry.TryAdd(MsgGrantAuthorization.MsgExec.ToLower(), (any) => any.Unpack()); + Registry.TryAdd(MsgGrantAuthorization.MsgRevoke.ToLower(), (any) => any.Unpack()); + Registry.TryAdd(MsgTypeNames.GenericAuthorization.ToLower(), (any) => any.Unpack()); + + // Cosmos.Bank.V1Beta1 + Registry.TryAdd(MsgGrantAuthorization.MsgSend.ToLower(), (any) => any.Unpack()); + Registry.TryAdd(MsgTypeNames.SendAuthorization.ToLower(), (any) => any.Unpack()); + Registry.TryAdd(MsgGrantAuthorization.MsgMultiSend.ToLower(), (any) => any.Unpack()); + + // Secret.Compute.V1Beta1 + Registry.TryAdd(MsgGrantAuthorization.MsgExecuteContract.ToLower(), (any) => any.Unpack()); + Registry.TryAdd(MsgGrantAuthorization.MsgInstantiateContract.ToLower(), (any) => any.Unpack()); + Registry.TryAdd(MsgGrantAuthorization.MsgStoreCode.ToLower(), (any) => any.Unpack()); + + // Cosmos.Crisis.V1Beta1 + Registry.TryAdd(MsgGrantAuthorization.MsgVerifyInvariant.ToLower(), (any) => any.Unpack()); + + // Cosmos.Distribution.V1Beta1 + Registry.TryAdd(MsgGrantAuthorization.MsgSetWithdrawAddress.ToLower(), (any) => any.Unpack()); + Registry.TryAdd(MsgGrantAuthorization.MsgWithdrawDelegatorReward.ToLower(), (any) => any.Unpack()); + Registry.TryAdd(MsgGrantAuthorization.MsgWithdrawValidatorCommission.ToLower(), (any) => any.Unpack()); + Registry.TryAdd(MsgGrantAuthorization.MsgFundCommunityPool.ToLower(), (any) => any.Unpack()); + + // Cosmos.Evidence.V1Beta1 + Registry.TryAdd(MsgGrantAuthorization.MsgSubmitEvidence.ToLower(), (any) => any.Unpack()); + + // Cosmos.Feegrant.V1Beta1 + Registry.TryAdd(MsgGrantAuthorization.MsgGrantAllowance.ToLower(), (any) => any.Unpack()); + Registry.TryAdd(MsgGrantAuthorization.MsgRevokeAllowance.ToLower(), (any) => any.Unpack()); + + // Cosmos.Gov.V1Beta1 + Registry.TryAdd(MsgGrantAuthorization.MsgSubmitProposal.ToLower(), (any) => any.Unpack()); + Registry.TryAdd(MsgGrantAuthorization.MsgVote.ToLower(), (any) => any.Unpack()); + Registry.TryAdd(MsgGrantAuthorization.MsgVoteWeighted.ToLower(), (any) => any.Unpack()); + Registry.TryAdd(MsgGrantAuthorization.MsgDeposit.ToLower(), (any) => any.Unpack()); + + // Ibc.Core.Channel.V1 + Registry.TryAdd(MsgGrantAuthorization.MsgRecvPacket.ToLower(), (any) => any.Unpack()); + Registry.TryAdd(MsgGrantAuthorization.MsgTimeout.ToLower(), (any) => any.Unpack()); + Registry.TryAdd(MsgGrantAuthorization.MsgTimeoutOnClose.ToLower(), (any) => any.Unpack()); + Registry.TryAdd(MsgGrantAuthorization.MsgChannelOpenInit.ToLower(), (any) => any.Unpack()); + Registry.TryAdd(MsgGrantAuthorization.MsgAcknowledgement.ToLower(), (any) => any.Unpack()); + Registry.TryAdd(MsgGrantAuthorization.MsgChannelOpenTry.ToLower(), (any) => any.Unpack()); + Registry.TryAdd(MsgGrantAuthorization.MsgChannelOpenAck.ToLower(), (any) => any.Unpack()); + Registry.TryAdd(MsgGrantAuthorization.MsgChannelOpenConfirm.ToLower(), (any) => any.Unpack()); + Registry.TryAdd(MsgGrantAuthorization.MsgChannelCloseInit.ToLower(), (any) => any.Unpack()); + Registry.TryAdd(MsgGrantAuthorization.MsgChannelCloseConfirm.ToLower(), (any) => any.Unpack()); + + // Ibc.Core.Client.V1 + Registry.TryAdd(MsgGrantAuthorization.MsgUpgradeClient.ToLower(), (any) => any.Unpack()); + Registry.TryAdd(MsgGrantAuthorization.MsgSubmitMisbehaviour.ToLower(), (any) => any.Unpack()); + Registry.TryAdd(MsgGrantAuthorization.MsgCreateClient.ToLower(), (any) => any.Unpack()); + + // Ibc.Applications.Transfer.V1 + Registry.TryAdd(MsgGrantAuthorization.MsgTransfer.ToLower(), (any) => any.Unpack()); + + // Secret.Registration.V1Beta1 + Registry.TryAdd(MsgTypeNames.RaAuthenticate.ToLower(), (any) => any.Unpack()); + + // Cosmos.Slashing.V1Beta1 + Registry.TryAdd(MsgGrantAuthorization.MsgUnjail.ToLower(), (any) => any.Unpack()); + + // Cosmos.Staking.V1Beta1 + Registry.TryAdd(MsgGrantAuthorization.MsgCreateValidator.ToLower(), (any) => any.Unpack()); + Registry.TryAdd(MsgGrantAuthorization.MsgEditValidator.ToLower(), (any) => any.Unpack()); + Registry.TryAdd(MsgGrantAuthorization.MsgDelegate.ToLower(), (any) => any.Unpack()); + Registry.TryAdd(MsgGrantAuthorization.MsgBeginRedelegate.ToLower(), (any) => any.Unpack()); + Registry.TryAdd(MsgGrantAuthorization.MsgUndelegate.ToLower(), (any) => any.Unpack()); + Registry.TryAdd(MsgTypeNames.StakeAuthorization.ToLower(), (any) => any.Unpack()); + + // Cosmos.Vesting.V1Beta1 + Registry.TryAdd(MsgTypeNames.MsgCreateVestingAccount.ToLower(), (any) => any.Unpack()); + + + + + + } + + public static Func Get(string typeUrl) + { + if (!String.IsNullOrEmpty(typeUrl)) + { + var regKey = typeUrl.CleanUpTypeurl().ToLower(); + if (Registry.ContainsKey(regKey)) + { + return Registry[regKey]; + } + } + return null; + } +} diff --git a/src/Tx/MsgTypeNames.cs b/src/Tx/MsgTypeNames.cs index 856e99d8..d001246e 100644 --- a/src/Tx/MsgTypeNames.cs +++ b/src/Tx/MsgTypeNames.cs @@ -85,5 +85,4 @@ public static class MsgTypeNames public const string GenericAuthorization = "cosmos.authz.v1beta1.GenericAuthorization"; public const string RaAuthenticate = "secret.registration.v1beta1.RaAuthenticate"; public const string MsgCreateVestingAccount = "cosmos.vesting.v1beta1.MsgCreateVestingAccount"; - public const string MsgSnip20Send = "MsgSnip20Send"; } \ No newline at end of file diff --git a/src/Tx/SecretTx.cs b/src/Tx/SecretTx.cs index baba28dc..b7019bc8 100644 --- a/src/Tx/SecretTx.cs +++ b/src/Tx/SecretTx.cs @@ -1,6 +1,7 @@ using Google.Protobuf.Collections; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using Cosmos.Base.Abci.V1Beta1; using System; using System.Collections.Generic; using System.Linq; @@ -11,7 +12,7 @@ namespace SecretNET.Tx { public class SecretTx { - public GetTxResponse EncryptedResponse { get; internal set; } + public TxResponse TxResponse { get; internal set; } #region EncryptedResponse reference props @@ -45,30 +46,41 @@ public class SecretTx public List Exceptions { get; internal set; } = new List(); + /// + /// Initializes a new instance of the class. + /// public SecretTx() { } + /// + /// Initializes a new instance of the class. + /// + /// The tx hash. public SecretTx(string txHash) { Txhash = txHash; Success = true; } - public SecretTx(GetTxResponse response) + /// + /// Initializes a new instance of the class. + /// + /// The response. + public SecretTx(TxResponse response) { - EncryptedResponse = response; - if (EncryptedResponse?.TxResponse != null) + TxResponse = response; + if (TxResponse != null) { - Txhash = EncryptedResponse?.TxResponse?.Txhash; - Codespace = EncryptedResponse?.TxResponse?.Codespace; - Height = EncryptedResponse.TxResponse.Height; - Code = EncryptedResponse.TxResponse.Code; - TxBytes = EncryptedResponse.TxResponse.Tx.Value.ToByteArray(); - Events = EncryptedResponse.TxResponse.Events; - GasUsed = EncryptedResponse.TxResponse.GasUsed; - GasWanted = EncryptedResponse.TxResponse.GasWanted; + Txhash = TxResponse?.Txhash; + Codespace = TxResponse?.Codespace; + Height = TxResponse.Height; + Code = TxResponse.Code; + TxBytes = TxResponse.Tx?.Value?.ToByteArray(); + Events = TxResponse?.Events; + GasUsed = TxResponse.GasUsed; + GasWanted = TxResponse.GasWanted; } else { @@ -79,9 +91,13 @@ public SecretTx(GetTxResponse response) } } + /// + /// Initializes a new instance of the class. + /// + /// The secret tx. public SecretTx(SecretTx secretTx) { - EncryptedResponse = secretTx.EncryptedResponse; + TxResponse = secretTx.TxResponse; Height = secretTx.Height; Codespace = secretTx.Codespace; Code = secretTx.Code; @@ -98,26 +114,45 @@ public SecretTx(SecretTx secretTx) Exceptions = secretTx.Exceptions; } + /// + /// Initializes a new instance of the class. + /// + /// The ex. + /// The tx hash. public SecretTx(Exception ex, string txHash) { Txhash = txHash; Exceptions.Add(ex); } + /// + /// Gets the response MSG. + /// + /// + /// Index of the MSG. + /// T. public T GetResponseMsg(int msgIndex = 0) { - if (Data != null && Data.Count() > msgIndex) + var msgData = GetResponseData(msgIndex); + if (msgData != null) { - return JsonConvert.DeserializeObject(Encoding.UTF8.GetString(Data[msgIndex])); + return JsonConvert.DeserializeObject(Encoding.UTF8.GetString(msgData)); } return default; } + /// + /// Gets the response json. + /// + /// Index of the MSG. + /// if set to true [formated]. + /// System.String. public string GetResponseJson(int msgIndex = 0, bool formated = true) { - if (Data != null && Data.Count() > msgIndex) + var msgData = GetResponseData(msgIndex); + if (msgData != null) { - var json = Encoding.UTF8.GetString(Data[msgIndex]); + var json = Encoding.UTF8.GetString(msgData); if (formated) { return JToken.Parse(json).ToString(); @@ -127,12 +162,17 @@ public string GetResponseJson(int msgIndex = 0, bool formated = true) return null; } + /// + /// Tries the find event value. + /// + /// The event key. + /// System.String. public string TryFindEventValue(string eventKey) { string result = String.Empty; - if (EncryptedResponse?.TxResponse?.Logs != null) + if (TxResponse?.Logs != null) { - result = EncryptedResponse.TxResponse.Logs + result = TxResponse.Logs .Where(a => a.Events != null && a.Events.Any()) .Select(a => a.Events.FirstOrDefault(b => b.Attributes.Any(c => c.Key == eventKey))) .Select(a => a.Attributes.FirstOrDefault(b => b.Key == eventKey)?.Value) @@ -140,6 +180,32 @@ public string TryFindEventValue(string eventKey) } return result; } + + // private + internal byte[] GetResponseData(int msgIndex = 0) + { + if (TxResponse?.Tx != null) + { + var decodedTx = TxResponse.Tx.Unpack(); + if (decodedTx?.Body?.Messages?.Count -1 >= msgIndex) + { + var rawMsg = decodedTx.Body.Messages[msgIndex]; + if (rawMsg.TypeUrl.IsProtoType(MsgGrantAuthorization.MsgExecuteContract)) + { + var msg = Secret.Compute.V1Beta1.MsgExecuteContractResponse.Parser.ParseFrom(Data[msgIndex]); + if (msg != null) + { + return msg.Data.ToArray(); + } + } + else + { + return Data[msgIndex]; + } + } + } + return null; + } } public class SingleSecretTx : SecretTx @@ -155,9 +221,10 @@ private void ParseData() { try { - if (Data != null && Data.Count() > 0) + var msgData = GetResponseData(0); + if (msgData != null) { - var jsonData = Encoding.UTF8.GetString(Data[0]); + var jsonData = Encoding.UTF8.GetString(msgData); Response = JsonConvert.DeserializeObject(jsonData); } } diff --git a/src/Tx/TxClient.cs b/src/Tx/TxClient.cs index 6607158f..42e517ea 100644 --- a/src/Tx/TxClient.cs +++ b/src/Tx/TxClient.cs @@ -1,5 +1,8 @@ -using Cosmos.Base.Abci.V1Beta1; +using Cosmos.Tx.V1Beta1; +using Cosmos.Base.Abci.V1Beta1; using System.Text.RegularExpressions; +using System.Collections.Concurrent; +using Google.Protobuf; namespace SecretNET.Tx; @@ -53,7 +56,7 @@ public async Task Simulate(MsgBase[] messages, TxOptions txOpt var prepareResult = await secretClient.PrepareAndSign(messages, txOptions); var request = new SimulateRequest() { - TxBytes = ByteString.CopyFrom(prepareResult.TxBytes) + TxBytes = ByteString.CopyFrom(prepareResult) }; var result = await client.SimulateAsync(request); @@ -82,125 +85,267 @@ public async Task> Broadcast(MsgBase[] messages, TxOptions public async Task Broadcast(MsgBase[] messages, TxOptions txOptions = null) { - txOptions = txOptions != null ? txOptions : new TxOptions(); + txOptions = txOptions != null ? txOptions : new TxOptions(); + var prepareResult = await secretClient.PrepareAndSign(messages, txOptions); + + return await BroadcastTx(prepareResult, txOptions); + } + + private async Task BroadcastTx(byte[] TxBytes, TxOptions txOptions) + { var start = DateTime.Now; + var txHash = SecretNET.Crypto.Hashes.SHA256(TxBytes).ToHexString().ToUpper(); + + Console.WriteLine("BroadcastTx Calculated TxHash: " + txHash); + + var mode = txOptions.BroadcastMode; + var waitForCommit = txOptions.WaitForCommit; - var prepareResult = await secretClient.PrepareAndSign(messages, txOptions); var request = new BroadcastTxRequest() { - TxBytes = ByteString.CopyFrom(prepareResult.TxBytes), - Mode = txOptions?.BroadcastMode ?? BroadcastMode.Sync + TxBytes = ByteString.CopyFrom(TxBytes), + Mode = mode, }; - var result = await client.BroadcastTxAsync(request); - var txHash = result?.TxResponse?.Txhash; + BroadcastTxResponse broadcastResponse = null; - // Check for errors - if (result.TxResponse.Code > 0) + if (mode == BroadcastMode.Block) { - // Codespace SDK - // https://github.com/cosmos/cosmos-sdk/blob/main/types/errors/errors.go - if (result.TxResponse.Codespace.Equals("sdk", StringComparison.CurrentCultureIgnoreCase)) + waitForCommit = true; + + var isBroadcastTimedOut = false; + + try + { + broadcastResponse = await client.BroadcastTxAsync(request); + } + catch (Exception ex) { - var isEnumParsed = System.Enum.TryParse(result.TxResponse.Code.ToString(), true, out CosmosSdkErrorEnum errorEnum); - if (isEnumParsed) + if (ex.ToString().ToLower().Contains("timed out waiting for tx to be included in a block", StringComparison.CurrentCultureIgnoreCase)) { - return new SecretTx(new CosmosSdkException(errorEnum, result), txHash); + isBroadcastTimedOut = true; } } - else + + if (!isBroadcastTimedOut) { - if (request.Mode == BroadcastMode.Sync) + return await DecodeTxResponse(broadcastResponse?.TxResponse); + } + + } + else if (mode == BroadcastMode.Sync) + { + broadcastResponse = await client.BroadcastTxAsync(request); + + // Check for errors + if (broadcastResponse.TxResponse.Code > 0) + { + // Codespace SDK + // https://github.com/cosmos/cosmos-sdk/blob/main/types/errors/errors.go + if (broadcastResponse.TxResponse.Codespace.Equals("sdk", StringComparison.CurrentCultureIgnoreCase)) { - return new SecretTx(new Exception($"Broadcasting transaction failed with code {result?.TxResponse?.Code}(codespace: {result?.TxResponse?.Codespace}). Log: {result?.TxResponse?.RawLog}"), txHash); + var isEnumParsed = System.Enum.TryParse(broadcastResponse.TxResponse.Code.ToString(), true, out CosmosSdkErrorEnum errorEnum); + if (isEnumParsed) + { + return new SecretTx(new CosmosSdkException(errorEnum, broadcastResponse), txHash); + } + } + else + { + if (request.Mode == BroadcastMode.Sync) + { + return new SecretTx(new Exception($"Broadcasting transaction failed with code {broadcastResponse?.TxResponse?.Code}(codespace: {broadcastResponse?.TxResponse?.Codespace}). Log: {broadcastResponse?.TxResponse?.RawLog}"), txHash); + } } } } + else if (mode == BroadcastMode.Async) + { + broadcastResponse = await client.BroadcastTxAsync(request); + } + else + { + throw new Exception($"Unknown broadcast mode '{mode}', must be either {BroadcastMode.Block}, {BroadcastMode.Sync} or {BroadcastMode.Async}"); + } // no wait - if (!txOptions.WaitForCommit) + if (!waitForCommit) { return new SecretTx(txHash); } + var timeoutMs = txOptions.BroadcastTimeoutMs; + var checkIntervalMs = txOptions.BroadcastCheckIntervalMs; + // wait BroadcastCheckIntervalMs before checking the first time on chain - await Task.Delay(TimeSpan.FromMilliseconds(txOptions.BroadcastCheckIntervalMs)); + await Task.Delay(TimeSpan.FromMilliseconds(checkIntervalMs / 2)); while (true) { SecretTx txResult = null; - Func getTx = async (hash, nonces) => + Func getTx = async (hash) => { - txResult = await GetTx(hash, nonces); + txResult = await GetTx(hash); }; - await Task.WhenAny( - getTx(txHash, prepareResult.Nonces), - Task.Delay(TimeSpan.FromMilliseconds(txOptions.BroadcastCheckIntervalMs))); + await Task.WhenAny(getTx(txHash), Task.Delay(TimeSpan.FromMilliseconds(checkIntervalMs))); if (txResult != null) { return txResult; } - if (start + TimeSpan.FromMilliseconds(txOptions.BroadcastTimeoutMs) < DateTime.Now) + if (start + TimeSpan.FromMilliseconds(timeoutMs) < DateTime.Now) { throw new Exception($"Transaction ID {txHash} was submitted but was not yet found on the chain. You might want to check later."); } } } - private readonly Regex _errorMessageRegEx = new Regex("; message index: (?\\d+): encrypted: (?.+?): (?:instantiate|execute|query) contract failed", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline); + private readonly Regex _errorMessageRegEx = new Regex("; message index: (?\\d+):(?: dispatch: submessages:)* encrypted: (?.+?): (?:instantiate|execute|query) contract failed", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline); - public async Task GetTx(string hash, ComputeMsgToNonce nonces = null) + /// + /// Gets the tx. + /// + /// The hash. + /// if set to true [try to decrypt]. + /// SecretTx. + public async Task GetTx(string hash, bool tryToDecrypt = true) { - var request = new GetTxRequest() - { - Hash = hash - }; + var result = await TxsQuery($"tx.hash='{hash}'", tryToDecrypt); + return result?[0]; + } + - GetTxResponse encryptedResult = null; - try + /// + /// TXSs the query. + /// + /// The query. + /// if set to true [try to decrypt]. + /// SecretTx[]. + public async Task TxsQuery(string query, bool tryToDecrypt = false) + { + if (!String.IsNullOrWhiteSpace(query)) { - encryptedResult = await client.GetTxAsync(request); + GetTxsEventRequest request = new GetTxsEventRequest(); + request.Events.Add(query.Split(" AND ").Select(q => q.Trim()).ToList()); + + var result = await GetTxsEvent(request, tryToDecrypt); + return result; } - catch (RpcException rpcEx) + return null; + } + + /// + /// GetTxsEvent fetches txs by event. + /// + /// The request. + /// if set to true [try to decrypt]. + /// GetTxsEventResponse. + public async Task GetTxsEvent(GetTxsEventRequest request, bool tryToDecrypt = false) + { + var result = await client.GetTxsEventAsync(request); + + var decodedResponses = await this.DecodeTxResponses(result?.TxResponses?.ToArray(), tryToDecrypt); + return decodedResponses; + } + + + internal async Task DecodeTxResponse(TxResponse txResponse, bool tryToDecrypt = false) + { + if (txResponse == null) return null; + + var result = await DecodeTxResponses(new TxResponse[] { txResponse }, tryToDecrypt); + return result?[0]; + } + + internal async Task DecodeTxResponses(TxResponse[] txResponses, bool tryToDecrypt = false) + { + if (txResponses == null || txResponses.Length == 0) return null; + var result = new ConcurrentBag(); + + await Parallel.ForEachAsync(txResponses, async (txResponse, cancellationToken) => { - if (rpcEx.StatusCode == StatusCode.NotFound) - { - return null; - } - else + var decodedTx = txResponse.Tx.Unpack(); + + var nonces = new ComputeMsgToNonce(); + + // Decoded input tx messages + for (var i = 0; i < decodedTx?.Body?.Messages?.Count; i++) { - throw rpcEx; + var rawMsg = decodedTx.Body.Messages[i]; + + var messageDecoder = MsgDecoderRegistry.Get(rawMsg.TypeUrl); + if (messageDecoder == null) continue; + + var msg = messageDecoder(rawMsg); + + if (tryToDecrypt) + { + // Check if the message needs decryption + byte[] inputMsgBytes = null; + if (rawMsg.TypeUrl.IsProtoType(MsgGrantAuthorization.MsgInstantiateContract)) + { + inputMsgBytes = ((Secret.Compute.V1Beta1.MsgInstantiateContract)msg).InitMsg.Span.ToArray(); + } + else if (rawMsg.TypeUrl.IsProtoType(MsgGrantAuthorization.MsgExecuteContract)) + { + inputMsgBytes = ((Secret.Compute.V1Beta1.MsgExecuteContract)msg).Msg.Span.ToArray(); + } + + if (inputMsgBytes != null && inputMsgBytes.Length > 0) + { + // Encrypted, try to decrypt + try + { + var nonce = new ArraySegment(inputMsgBytes, 0, 32).ToArray(); + //Console.WriteLine("DecodeTxResponses nonce: " + nonce.ToHexString()); + + var accountPubkey = new ArraySegment(inputMsgBytes, 32, 32).ToArray(); // unused in decryption + var ciphertext = new ArraySegment(inputMsgBytes, 64, inputMsgBytes.Length - 64).ToArray(); + var plaintext = await Encryption.Decrypt(ciphertext, nonce); + + if (rawMsg.TypeUrl.IsProtoType(MsgGrantAuthorization.MsgInstantiateContract)) + { + ((Secret.Compute.V1Beta1.MsgInstantiateContract)msg).InitMsg = ByteString.CopyFrom(new ArraySegment(plaintext, 64, plaintext.Length - 64).ToArray()); + } + else if (rawMsg.TypeUrl.IsProtoType(MsgGrantAuthorization.MsgExecuteContract)) + { + ((Secret.Compute.V1Beta1.MsgExecuteContract)msg).Msg = ByteString.CopyFrom(new ArraySegment(plaintext, 64, plaintext.Length - 64).ToArray()); + } + + nonces[i] = nonce; // Fill nonces array to later use it in output decryption + + } + catch (Exception ex) + { + // Not encrypted or can't decrypt because not original sender + } + } + } + + decodedTx.Body.Messages[i] = Any.Pack(msg); } - } - if (encryptedResult == null) - { - return null; - } + txResponse.Tx = Any.Pack(decodedTx); - var secretTx = new SecretTx(encryptedResult); + var secretTx = new SecretTx(txResponse); - Func getNounce = (msgIndex) => - { - if (msgIndex < nonces.Count()) + Func getNounce = (msgIndex) => { - var nonce = nonces[msgIndex]; - if (nonce != null && nonce.Length == 32) + if (msgIndex < nonces.Count()) { - return nonce; + var nonce = nonces[msgIndex]; + if (nonce != null && nonce.Length == 32) + { + return nonce; + } } - } - return null; - }; + return null; + }; - // decode result - if (encryptedResult != null && encryptedResult.TxResponse != null && nonces != null) - { - var tx = encryptedResult.TxResponse; + var tx = txResponse; List jsonLogs = null; var arrayLog = new List(); var rawLog = (string)tx.RawLog.Clone(); @@ -222,7 +367,7 @@ public async Task GetTx(string hash, ComputeMsgToNonce nonces = null) foreach (var attr in ev.Attributes) { // Try to decrypt - if (ev.MessageType.Equals("wasm", StringComparison.CurrentCultureIgnoreCase)) + if (tryToDecrypt && ev.MessageType.Equals("wasm", StringComparison.CurrentCultureIgnoreCase)) { var nonce = getNounce(log.MsgIndex.GetValueOrDefault()); if (nonce != null) @@ -232,7 +377,7 @@ public async Task GetTx(string hash, ComputeMsgToNonce nonces = null) if (attr.Key.IsBase64String()) { attr.Key = Encoding.UTF8.GetString(await Encryption.Decrypt(Convert.FromBase64String(attr.Key), nonce)); - } + } } catch { } try @@ -240,11 +385,11 @@ public async Task GetTx(string hash, ComputeMsgToNonce nonces = null) if (attr.Value.IsBase64String()) { attr.Value = Encoding.UTF8.GetString(await Encryption.Decrypt(Convert.FromBase64String(attr.Value), nonce)); - } + } } catch { } } - } + } arrayLog.Add(new CosmosArrayLog() { @@ -285,7 +430,7 @@ public async Task GetTx(string hash, ComputeMsgToNonce nonces = null) var isEnumParsed = System.Enum.TryParse(tx.Code.ToString(), true, out CosmosSdkErrorEnum errorEnum); if (isEnumParsed) { - secretTx.Exceptions.Add(new CosmosSdkException(errorEnum, encryptedResult)); + secretTx.Exceptions.Add(new CosmosSdkException(errorEnum, txResponse)); //throw new CosmosSdkException(errorEnum, encryptedResult); } } @@ -302,23 +447,45 @@ public async Task GetTx(string hash, ComputeMsgToNonce nonces = null) var data = new byte[txMsgData.Data.Count][]; for (int msgIndex = 0; msgIndex < txMsgData.Data.Count; msgIndex++) { - var msgData = txMsgData.Data[msgIndex].Data.ToByteArray(); - if (msgData.Length > 0) + data[msgIndex] = txMsgData.Data[msgIndex].Data.ToByteArray(); + if (data[msgIndex]?.Length > 0) { var nonce = getNounce(msgIndex); - if (nonce != null) + if (nonce != null && tryToDecrypt) { - try + var rawMsg = decodedTx?.Body?.Messages[msgIndex]; + var messageDecoder = MsgDecoderRegistry.Get(rawMsg.TypeUrl); + if (messageDecoder == null) continue; + + var msg = messageDecoder(rawMsg); + + if (rawMsg.TypeUrl.IsProtoType(MsgGrantAuthorization.MsgInstantiateContract)) { - var decryptedDataBase64 = Encoding.UTF8.GetString(await Encryption.Decrypt(txMsgData.Data[msgIndex].Data.ToByteArray(), nonce)); - data[msgIndex] = Convert.FromBase64String(decryptedDataBase64); + var decoded = Secret.Compute.V1Beta1.MsgInstantiateContractResponse.Parser.ParseFrom(txMsgData.Data[msgIndex].Data); + var decrypted = Convert.FromBase64String(Encoding.UTF8.GetString(await Encryption.Decrypt(decoded.Data.ToByteArray(), nonce))); + var decryptedMsg = new Secret.Compute.V1Beta1.MsgInstantiateContractResponse() + { + Address = decoded.Address, + Data = ByteString.CopyFrom(decrypted) + }; + data[msgIndex] = decryptedMsg.Encode(); } - catch (Exception ex) + else if (rawMsg.TypeUrl.IsProtoType(MsgGrantAuthorization.MsgExecuteContract)) { - // Not encrypted or can't decrypt because not original sender - data[msgIndex] = txMsgData.Data[msgIndex].Data.ToByteArray(); + var decoded = Secret.Compute.V1Beta1.MsgExecuteContractResponse.Parser.ParseFrom(txMsgData.Data[msgIndex].Data); + var decrypted = Convert.FromBase64String(Encoding.UTF8.GetString(await Encryption.Decrypt(decoded.Data.ToByteArray(), nonce))); + var jsonData = Encoding.UTF8.GetString(decrypted); + var decryptedMsg = new Secret.Compute.V1Beta1.MsgExecuteContractResponse() + { + Data = ByteString.CopyFrom(decrypted) + }; + data[msgIndex] = decryptedMsg.Encode(); } } + else + { + data[msgIndex] = txMsgData.Data[msgIndex].Data.ToByteArray(); + } } } @@ -327,9 +494,17 @@ public async Task GetTx(string hash, ComputeMsgToNonce nonces = null) secretTx.ArrayLog = arrayLog; secretTx.Data = data; secretTx.Success = true; + + result.Add(secretTx); + + }); + + if (!result.IsEmpty) + { + return result.ToArray(); } - return secretTx; + return null; } } diff --git a/src/Tx/TxOptions.cs b/src/Tx/TxOptions.cs index 82a6f072..4aa20467 100644 --- a/src/Tx/TxOptions.cs +++ b/src/Tx/TxOptions.cs @@ -29,6 +29,12 @@ public class TxOptions /// The fee denom. public string FeeDenom { get; set; } = "uscrt"; + /// + /// Address of the fee granter from which to charge gas fees. + /// + /// The fee granter. + public string FeeGranter { get; set; } + /// /// Defaults to "". /// From 96acb619948216d22590aa04602768534fef7395 Mon Sep 17 00:00:00 2001 From: 0xxCodemonkey Date: Sun, 18 Sep 2022 06:14:17 +0200 Subject: [PATCH 03/31] Update nuget infos --- src/SecretNET.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/SecretNET.csproj b/src/SecretNET.csproj index 69064853..e8097f08 100644 --- a/src/SecretNET.csproj +++ b/src/SecretNET.csproj @@ -21,7 +21,7 @@ True True Secret.NET (port of the secret.js Client) is an .NET SDK for writing applications that interact with the Secret Network blockchain. - 0.3.0 + 0.3.0-alpha NuGet_README.md https://github.com/0xxCodemonkey/SecretNET blockchain;privacy @@ -31,7 +31,7 @@ Secret.NET (port of the secret.js Client) is an .NET SDK for writing applications that interact with the Secret Network blockchain. Codemonkey Codemonkey - Shockwave Delta Update + CosmWasm v1 and adaptation to secretjs v.1.4 changes From 7173d6bd3ff740125d2745688d8668d0a0b7843a Mon Sep 17 00:00:00 2001 From: 0xxCodemonkey Date: Sun, 18 Sep 2022 07:38:32 +0200 Subject: [PATCH 04/31] Cleanup, docu --- src/Common/CreateClientOptions.cs | 2 +- src/Crypto/Miscreant/Block.cs | 5 +---- src/Crypto/Miscreant/Interfaces.cs | 4 ++-- src/Crypto/Miscreant/Siv.cs | 25 ++++--------------------- src/Crypto/Secp256k1/WNAF.cs | 2 +- src/Tx/TxOptions.cs | 6 +++--- 6 files changed, 12 insertions(+), 32 deletions(-) diff --git a/src/Common/CreateClientOptions.cs b/src/Common/CreateClientOptions.cs index a3a9776e..ba91df51 100644 --- a/src/Common/CreateClientOptions.cs +++ b/src/Common/CreateClientOptions.cs @@ -18,7 +18,7 @@ public class CreateClientOptions public string ChainId { get; private set; } /// - /// A wallet for signing transactions & permits. When `wallet` is supplied, `walletAddress` and `chainId` must be supplied too. + /// A wallet for signing transactions and permits. When `wallet` is supplied, `walletAddress` and `chainId` must be supplied too. /// /// The wallet. public Wallet Wallet { get; set; } diff --git a/src/Crypto/Miscreant/Block.cs b/src/Crypto/Miscreant/Block.cs index 0127072c..14e05e4d 100644 --- a/src/Crypto/Miscreant/Block.cs +++ b/src/Crypto/Miscreant/Block.cs @@ -59,10 +59,7 @@ internal void Copy(Block other) } /// - /// Double a value over GF(2^128): - /// - /// a<<1 if firstbit(a)=0 - /// (a<<1) ⊕ 0¹²⁰10000111 if firstbit(a)=1 + /// Double a value over GF(2^128) /// internal void Dbl() { diff --git a/src/Crypto/Miscreant/Interfaces.cs b/src/Crypto/Miscreant/Interfaces.cs index 1dc66124..445e6103 100644 --- a/src/Crypto/Miscreant/Interfaces.cs +++ b/src/Crypto/Miscreant/Interfaces.cs @@ -46,8 +46,8 @@ internal interface IMACLike /// public interface ISIVLike { - public byte[] Seal(byte[] plaintext, byte[][] associatedData); - public byte[] Open(byte[] plaintext, byte[][] associatedData); + public byte[] Seal(byte[] plaintext); + public byte[] Open(byte[] plaintext); public ISIVLike Clear(); } diff --git a/src/Crypto/Miscreant/Siv.cs b/src/Crypto/Miscreant/Siv.cs index 649ed239..1bb0a4ec 100644 --- a/src/Crypto/Miscreant/Siv.cs +++ b/src/Crypto/Miscreant/Siv.cs @@ -58,25 +58,17 @@ private Siv(IMACLike mac, ICTRLike ctr) /// Encrypt and authenticate data using AES-SIV /// /// The plaintext. - /// The associated data. - /// The . /// System.Byte[]. /// /// - public byte[] Seal(byte[] plaintext, byte[][] associatedData = null) + public byte[] Seal(byte[] plaintext) { - associatedData = new byte[][] { new byte[0] }; - if (associatedData.Length > MAX_ASSOCIATED_DATA) - { - throw new Exception("AES-SIV: too many associated data items"); - } - // Allocate space for sealed ciphertext. var resultLength = Block.SIZE + plaintext.Length; var result = new byte[resultLength]; // Authenticate. - var iv = S2V(plaintext, associatedData); + var iv = S2V(plaintext, new byte[][] { new byte[0] }); result.Set(iv); // Encrypt. @@ -90,20 +82,11 @@ public byte[] Seal(byte[] plaintext, byte[][] associatedData = null) /// Decrypt and authenticate data using AES-SIV /// /// The sealed text. - /// The associated data. /// System.Byte[]. - /// AES-SIV: too many associated data items /// AES-SIV: ciphertext is truncated /// AES-SIV: ciphertext verification failure! - /// - public byte[] Open(byte[] sealedText, byte[][]? associatedData = null) + public byte[] Open(byte[] sealedText) { - associatedData = new byte[][] { new byte[0] }; - if (associatedData.Length > MAX_ASSOCIATED_DATA) - { - throw new Exception("AES-SIV: too many associated data items"); - } - if (sealedText.Length < Block.SIZE) { throw new Exception("AES-SIV: ciphertext is truncated"); @@ -120,7 +103,7 @@ public byte[] Open(byte[] sealedText, byte[][]? associatedData = null) var result = _ctr.EncryptCtr(iv, sealedText.Subarray(Block.SIZE)); // Authenticate. - var expectedTag = S2V(result, associatedData); + var expectedTag = S2V(result, new byte[][] { new byte[0] }); if (!expectedTag.SequenceEqual(tag)) { diff --git a/src/Crypto/Secp256k1/WNAF.cs b/src/Crypto/Secp256k1/WNAF.cs index 68562934..58910cdc 100644 --- a/src/Crypto/Secp256k1/WNAF.cs +++ b/src/Crypto/Secp256k1/WNAF.cs @@ -93,7 +93,7 @@ public static int Const(Span wnaf, Scalar s, int w, int size) /** Convert a number to WNAF notation. * The number becomes represented by sum(2^{wi} * wnaf[i], i=0..WNAF_SIZE(w)+1) - return_val. * It has the following guarantees: - * - each wnaf[i] is either 0 or an odd integer between -(1 << w) and (1 << w) + * - each wnaf[i] is either 0 or an odd integer between -(1) and (1) * - the number of words set is always WNAF_SIZE(w) * - the returned skew is 0 or 1 */ diff --git a/src/Tx/TxOptions.cs b/src/Tx/TxOptions.cs index 4aa20467..4f019186 100644 --- a/src/Tx/TxOptions.cs +++ b/src/Tx/TxOptions.cs @@ -18,7 +18,7 @@ public class TxOptions public int GasLimit { get; set; } = 25000; /// - /// E.g. gasPriceInFeeDenom=0.25 & feeDenom="uscrt" => Total fee for tx is `0.25 * gasLimit`uscrt. Defaults to `0.25`. + /// E.g. gasPriceInFeeDenom=0.25 and feeDenom="uscrt" => Total fee for tx is `0.25 * gasLimit`uscrt. Defaults to `0.25`. /// /// The gas price in fee denom. public float GasPriceInFeeDenom { get; set; } = 0.25F; @@ -73,8 +73,8 @@ public class TxOptions public BroadcastMode BroadcastMode { get; set; } = BroadcastMode.Sync; /// - /// explicitSignerData can be used to override `chainId`, `accountNumber` & `accountSequence`. - /// This is usefull when using {@link BroadcastMode.Async} or when you don't want secretNET to query for `accountNumber` & `accountSequence` from the chain. (smoother in UIs, less load on your node provider). + /// ExplicitSignerData can be used to override `chainId`, `accountNumber` and `accountSequence`. + /// This is usefull when using BroadcastMode.Async or when you don't want secretNET to query for `accountNumber` and `accountSequence` from the chain. (smoother in UIs, less load on your node provider). /// /// The explicit signer data. public SignerData ExplicitSignerData { get; set; } From 723551a1c348d9f3ec5d1cd9965c4254ecea16e5 Mon Sep 17 00:00:00 2001 From: Codemonkey <107538234+0xxCodemonkey@users.noreply.github.com> Date: Tue, 20 Sep 2022 08:34:42 +0200 Subject: [PATCH 05/31] Update README.md --- README.md | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/README.md b/README.md index 38918fc9..7d888eed 100644 --- a/README.md +++ b/README.md @@ -161,6 +161,80 @@ Later via prop: secretNetworkClient.Wallet = walletFromMnemonic; ``` ## Sending Queries +### `secretClient.Query` + +#### `secretClient.Query.GetTx(string hash, bool tryToDecrypt = true)` +Returns a transaction with a txhash. `hash` is a 64 character upper-case hex string. + +If set the parameter tryToDecrypt to true (default) the client tries to decrypt the tx data (works only if the tx was created in the same session / client instance or if the same CreateClientOptions.EncryptionSeed is used). + +#### `secretClient.Query.TxsQuery(string query, bool tryToDecrypt = false)` +Returns all transactions that match a query. + +If set the parameter tryToDecrypt to true (default = false) the client tries to decrypt the tx data (works only if the tx was created in the same session / client instance or if the same CreateClientOptions.EncryptionSeed is used). + +To tell which events you want, you need to provide a query. query is a string, which has a form: `condition AND condition ...` (no OR at the moment). Condition has a form: `key operation operand`. key is a string with a restricted set of possible symbols (`\t`, `\n`, `\r`, `\`, `(`, `)`, `"`, `'`, `=`, `>`, `<` are not allowed). Operation can be `=`, `<`, `<=`, `>`, `>=`, `CONTAINS` AND `EXISTS`. Operand can be a string (escaped with single quotes), number, date or time. + +Examples: + +- `tx.hash = 'XYZ'` # single transaction +- `tx.height = 5` # all txs of the fifth block +- `create_validator.validator = 'ABC'` # tx where validator ABC was created + +Tendermint provides a few predefined keys: `tx.hash` and `tx.height`. You can provide additional event keys that were emitted during the transaction. All events are indexed by a composite key of the form `{eventType}.{evenAttrKey}`. Multiple event types with duplicate keys are allowed and are meant to categorize unique and distinct events. + +To create a query for txs where AddrA transferred funds: `transfer.sender = 'AddrA'` + +See `txsQuery` under https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.Queries.htm + +### Get SCRT Balance + +```csharp +var response = await secretClient.Query.Bank.Balance("secret1ap26qrlp8mcq2pg6r47w43l0y8zkqm8a450s03"); +Console.WriteLine("Balance: " + (float.Parse(response.Amount) / 1000000f)); // denom: "uscrt" +``` + +### Query smart contract (permissionless method) + +```csharp +// grpcWebUrl => TODO get from https://github.com/scrtlabs/api-registry +// chainId e.g. "secret-4" or "pulsar-2" +var secretClient = new SecretNetworkClient(grpcWebUrl, chainId, wallet: null); + +// https://github.com/scrtlabs/mysimplecounter +var contractAddress = "secret12j8e6aeyy9ff7drfks3knxva0pxj847fle8zsf"; // pulsar-2 +var contractCodeHash = "3d528d0d9889d5887abd3e497243ed6e5a4da008091e20ee420eca39874209ad"; + +var getCountQueryMsg = new { get_count = new { } }; + +var queryContractResult = await secretClient.Query.Compute.QueryContract( + contractAddress: contractAddress, + queryMsg: getCountQueryMsg, + codeHash: contractCodeHash); // optional but way faster + +Console.WriteLine(queryContractResult.Response); // JSON string +``` + +Or even easier for a **SNIP20 Contract** via **SecretNET.SNIP20** Add-On: + +```csharp +var snip20Client = new SecretNET.SNIP20.Snip20Client(secretClient); + +var sSCRT_Address = "secret1k0jntykt7e4g3y88ltc60czgjuqdy4c9e8fzek"; // secret-4 +var sScrt_CodeHash = "af74387e276be8874f07bec3a87023ee49b0e7ebe08178c49d0a49c3c98ed60e"; + +var tokenInfoResult = (await snip20Client.Query.GetTokenInfo( + contractAddress: sSCRT_Address, + codeHash: sScrt_CodeHash // optional but way faster + )).Response.Result; + +Console.WriteLine($"TokenName: {tokenInfoResult.Name}, Symbol: {tokenInfoResult.Symbol}"); +``` +#### Other queries eg. account, auth, bank, etc. +##### secretClient.Query.Auth +- `Account(string address)` => Returns account details based on address. +- `Accounts()` => Returns all existing accounts on the blockchain. + ## Broadcasting Transactions ## Uploading and initialize Smart Contract ## Calling a Smart Contract From 5f8fca118fe2c495abbd47ba29e16d1e20d966a3 Mon Sep 17 00:00:00 2001 From: 0xxCodemonkey Date: Tue, 20 Sep 2022 08:37:33 +0200 Subject: [PATCH 06/31] Add GetTx, TxsQuery and GetTxsEvent method ref to Queries class. --- src/Query/AuthQueryClient.cs | 9 ++++++++ src/Query/Queries.cs | 41 +++++++++++++++++++++++++++++++++++- src/Tx/TxClient.cs | 6 +++--- 3 files changed, 52 insertions(+), 4 deletions(-) diff --git a/src/Query/AuthQueryClient.cs b/src/Query/AuthQueryClient.cs index 0125f957..8d350e6d 100644 --- a/src/Query/AuthQueryClient.cs +++ b/src/Query/AuthQueryClient.cs @@ -43,6 +43,15 @@ public async Task Account(string address) return null; } + /// + /// Accountses this instance. + /// + /// List<IMessage>. + public async Task> Accounts() + { + return await Accounts(new QueryAccountsRequest()); + } + /// /// Returns all the existing accounts /// diff --git a/src/Query/Queries.cs b/src/Query/Queries.cs index 89a32f30..cade4b63 100644 --- a/src/Query/Queries.cs +++ b/src/Query/Queries.cs @@ -2,6 +2,8 @@ public class Queries : GprcBase { + private Tx.TxClient _txClient; + // cosmos private AuthQueryClient _authQuery; private AuthzQueryClient _authzQuery; @@ -31,10 +33,12 @@ public class Queries : GprcBase /// Initializes a new instance of the class. /// /// The secret network client. + /// The tx client. /// The GRPC channel. /// The GRPC message interceptor. - internal Queries(ISecretNetworkClient secretNetworkClient, GrpcChannel grpcChannel, CallInvoker? grpcMessageInterceptor) : base(secretNetworkClient, grpcChannel, grpcMessageInterceptor) + internal Queries(ISecretNetworkClient secretNetworkClient, Tx.TxClient txClient, GrpcChannel grpcChannel, CallInvoker? grpcMessageInterceptor) : base(secretNetworkClient, grpcChannel, grpcMessageInterceptor) { + _txClient = txClient; _authQuery = new AuthQueryClient(secretNetworkClient, grpcChannel, grpcMessageInterceptor); _authzQuery = new AuthzQueryClient(secretNetworkClient, grpcChannel, grpcMessageInterceptor); _bankQuery = new BankQueryClient(secretNetworkClient, grpcChannel, grpcMessageInterceptor); @@ -56,6 +60,41 @@ internal Queries(ISecretNetworkClient secretNetworkClient, GrpcChannel grpcChann _upgradeQuery = new UpgradeQueryClient(secretNetworkClient, grpcChannel, grpcMessageInterceptor); } + /// + /// Gets the tx. + /// + /// The hash. + /// if set to true the client tries to decrypt the tx data (works only if the tx was created in the same session / client instance or if the same CreateClientOptions.EncryptionSeed is used). + /// SecretTx. + public async Task GetTx(string hash, bool tryToDecrypt = true) + { + return await _txClient.GetTx(hash, tryToDecrypt); + } + + /// + /// TXSs the query. + /// + /// The query. + /// if set to true the client tries to decrypt the tx data (works only if the tx was created in the same session / client instance or if the same CreateClientOptions.EncryptionSeed is used). + /// SecretTx[]. + public async Task TxsQuery(string query, bool tryToDecrypt = false) + { + return await _txClient.TxsQuery(query, tryToDecrypt); + } + + /// + /// GetTxsEvent fetches txs by event. + /// + /// The request. + /// if set to true the client tries to decrypt the tx data (works only if the tx was created in the same session / client instance or if the same CreateClientOptions.EncryptionSeed is used). + /// GetTxsEventResponse. + public async Task GetTxsEvent(GetTxsEventRequest request, bool tryToDecrypt = false) + { + return await _txClient.GetTxsEvent(request, tryToDecrypt); + } + + + /// /// AuthQuerier is the query interface for the x/auth module /// diff --git a/src/Tx/TxClient.cs b/src/Tx/TxClient.cs index 42e517ea..1accd5f5 100644 --- a/src/Tx/TxClient.cs +++ b/src/Tx/TxClient.cs @@ -209,7 +209,7 @@ private async Task BroadcastTx(byte[] TxBytes, TxOptions txOptions) /// Gets the tx. /// /// The hash. - /// if set to true [try to decrypt]. + /// if set to true the client tries to decrypt the tx data (works only if the tx was created in the same session / client instance or if the same CreateClientOptions.EncryptionSeed is used). /// SecretTx. public async Task GetTx(string hash, bool tryToDecrypt = true) { @@ -222,7 +222,7 @@ public async Task GetTx(string hash, bool tryToDecrypt = true) /// TXSs the query. /// /// The query. - /// if set to true [try to decrypt]. + /// if set to true the client tries to decrypt the tx data (works only if the tx was created in the same session / client instance or if the same CreateClientOptions.EncryptionSeed is used). /// SecretTx[]. public async Task TxsQuery(string query, bool tryToDecrypt = false) { @@ -241,7 +241,7 @@ public async Task TxsQuery(string query, bool tryToDecrypt = false) /// GetTxsEvent fetches txs by event. /// /// The request. - /// if set to true [try to decrypt]. + /// if set to true the client tries to decrypt the tx data (works only if the tx was created in the same session / client instance or if the same CreateClientOptions.EncryptionSeed is used). /// GetTxsEventResponse. public async Task GetTxsEvent(GetTxsEventRequest request, bool tryToDecrypt = false) { From 88822b97c87ef9e57e1d179d96c93089836a7d83 Mon Sep 17 00:00:00 2001 From: Codemonkey <107538234+0xxCodemonkey@users.noreply.github.com> Date: Tue, 20 Sep 2022 16:07:03 +0200 Subject: [PATCH 07/31] Update queries infos --- README.md | 177 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 170 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 7d888eed..dfb2bc0b 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,7 @@ Install-Package SecretNET.UI (coming soon) ``` # Examples ## Creating / Initializing the wallet -When initializing a wallet, you must pass an *IPrivateKeyStorage* provider where the private key and mnemonic phrase will be stored (default = MauiSecureStorage). +When initializing a wallet, you must pass an ```IPrivateKeyStorage``` provider where the private key and mnemonic phrase will be stored (default = MauiSecureStorage). The following providers are available out of the box: - **MauiSecureStorage** @@ -160,18 +160,20 @@ Later via prop: ``` csharp secretNetworkClient.Wallet = walletFromMnemonic; ``` +# Querier (`secretClient.Query`) +The querier can only send queries and get chain information. Access to all query types can be done via ```SecretNetworkClient.Query```. + ## Sending Queries -### `secretClient.Query` -#### `secretClient.Query.GetTx(string hash, bool tryToDecrypt = true)` +### `secretClient.Query.GetTx(string hash, bool tryToDecrypt = true)` Returns a transaction with a txhash. `hash` is a 64 character upper-case hex string. If set the parameter tryToDecrypt to true (default) the client tries to decrypt the tx data (works only if the tx was created in the same session / client instance or if the same CreateClientOptions.EncryptionSeed is used). -#### `secretClient.Query.TxsQuery(string query, bool tryToDecrypt = false)` +### `secretClient.Query.TxsQuery(string query, bool tryToDecrypt = false)` Returns all transactions that match a query. -If set the parameter tryToDecrypt to true (default = false) the client tries to decrypt the tx data (works only if the tx was created in the same session / client instance or if the same CreateClientOptions.EncryptionSeed is used). +If set the parameter `tryToDecrypt` to true (default = false) the client tries to decrypt the tx data (works only if the tx was created in the same session / client instance or if the same ```CreateClientOptions.EncryptionSeed``` is used). To tell which events you want, you need to provide a query. query is a string, which has a form: `condition AND condition ...` (no OR at the moment). Condition has a form: `key operation operand`. key is a string with a restricted set of possible symbols (`\t`, `\n`, `\r`, `\`, `(`, `)`, `"`, `'`, `=`, `>`, `<` are not allowed). Operation can be `=`, `<`, `<=`, `>`, `>=`, `CONTAINS` AND `EXISTS`. Operand can be a string (escaped with single quotes), number, date or time. @@ -230,11 +232,172 @@ var tokenInfoResult = (await snip20Client.Query.GetTokenInfo( Console.WriteLine($"TokenName: {tokenInfoResult.Name}, Symbol: {tokenInfoResult.Symbol}"); ``` -#### Other queries eg. account, auth, bank, etc. -##### secretClient.Query.Auth +### All queries (eg. accounts, bank, compute, gov, feegrant, etc.) +- secretClient.Query.Auth +- secretClient.Query.Authz +- secretClient.Query.Bank +- secretClient.Query.Compute +- secretClient.Query.Distribution +- secretClient.Query.Evidence +- secretClient.Query.Feegrant +- secretClient.Query.Gov +- secretClient.Query.IbcChannel +- secretClient.Query.IbcClient +- secretClient.Query.IbcConnection +- secretClient.Query.IbcTransfer +- secretClient.Query.Mint +- secretClient.Query.Params +- secretClient.Query.Registration +- secretClient.Query.Slashing +- secretClient.Query.Staking +- secretClient.Query.Tendermint +- secretClient.Query.Upgrade + +See all details in the [**Full API »**](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.Queries.htm) + +#### [secretClient.Query.Auth](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.AuthQueryClient.htm) - `Account(string address)` => Returns account details based on address. - `Accounts()` => Returns all existing accounts on the blockchain. +#### [secretClient.Query.Authz](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.AuthzQueryClient.htm) +- `GranteeGrants(QueryGranteeGrantsRequest request)` => GranteeGrants returns a list of `GrantAuthorization` by grantee. Since: cosmos-sdk 0.45.2. +- `GranterGrants(QueryGranterGrantsRequest request)` => GranterGrants returns list of `GrantAuthorization`, granted by granter. Since: cosmos-sdk 0.45.2. +- `Grants(QueryGrantsRequest request)` => Returns list of `Authorization`, granted to the grantee by the granter. + +#### [secretClient.Query.Bank](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.BankQueryClient.htm) +- `Balance(QueryBalanceRequest request)` / `Balance(string address, string denom)` => Balance queries the balance of **a single** coin for a single account. +- `Balance(QueryAllBalancesRequest request)` => AllBalances queries the balance of **all coins** for a single account. +- `DenomMetadata(QueryDenomMetadataRequest request)` => DenomsMetadata queries the client metadata of **a given coin** denomination. +- `DenomsMetadata(QueryDenomsMetadataRequest request)` => DenomsMetadata queries the client metadata **for all registered** coin denominations. +- `Params(QueryParamsRequest request)` => Params queries the parameters of x/bank module. +- `SpendableBalances(QuerySpendableBalancesRequest request)` => SpendableBalances queries the spenable balance of all coins for a single. +- `SupplyOf(QuerySupplyOfRequest request)` => SupplyOf queries the supply of a single coin. +- `TotalSupply(QueryTotalSupplyRequest request)` => TotalSupply queries the total supply of all coins. + +#### [secretClient.Query.Compute](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.ComputeQueryClient.htm) +- `Code(ulong codeId, Metadata metadata)` => Get WASM bytecode and metadata for a code id. +- `Codes(Metadata metadata)` => Query all codes on chain. +- `ContractInfo(string contractAddress, Metadata metadata)` => Get metadata of a Secret Contract. +- `ContractsByCode(ulong codeId, Metadata metadata)` => Get all contracts that were instantiated from a code id. +- `GetCodeHash(string contractAddress, Metadata metadata)` => Get the codeHash of a Secret Contract. +- `GetCodeHashByCodeId(ulong codeId, Metadata metadata)` => Get the codeHash from a code id. +- `QueryContract(string contractAddress, Object queryMsg, string codeHash, Metadata metadata)` => Query a Secret Contract and cast the response as `R`. (see above) + +#### [secretClient.Query.Distribution](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.DistributionQueryClient.htm) +- `Balance(QueryParamsRequest request)` => CommunityPool queries the community pool coins. +- `CommunityPool(QueryCommunityPoolRequest request)` => Query all codes on chain. +- `DelegationRewards(QueryDelegationRewardsRequest request)` => DelegationRewards queries the total rewards accrued by a delegation. +- `DelegationTotalRewards(QueryDelegationTotalRewardsRequest request)` => DelegationTotalRewards queries the total rewards accrued by a each. +- `DelegatorValidators(QueryDelegatorValidatorsRequest request)` => DelegatorValidators queries the validators of a delegator. +- `DelegatorWithdrawAddress(QueryDelegatorWithdrawAddressRequest request)` => DelegatorWithdrawAddress queries withdraw address of a delegator. +- `FoundationTax(QueryFoundationTaxRequest request)` => FoundationTax queries. +- `ValidatorCommission(QueryValidatorCommissionRequest request)` => ValidatorCommission queries accumulated commission for a validator. +- `ValidatorOutstandingRewards(QueryValidatorCommissionRequest request)` => ValidatorOutstandingRewards queries rewards of a validator address. +- `ValidatorSlashes(QueryValidatorCommissionRequest request)` => ValidatorSlashes queries slash events of a validator. + +#### [secretClient.Query.Evidence](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.EvidenceQueryClient.htm) +- `AllEvidence(QueryAllEvidenceRequest request)` => AllEvidence queries all evidence. +- `FoundationTax(QueryEvidenceRequest request)` => Evidence queries evidence based on evidence hash. + +#### [secretClient.Query.Feegrant](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.FeegrantQueryClient.htm) +- `Allowance(QueryAllowanceRequest request)` => Allowance returns fee granted to the grantee by the granter. +- `Allowances(QueryAllowancesRequest request)` => Allowances returns all the grants for address. + +#### [secretClient.Query.Gov](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.GovQueryClient.htm) +- `Deposit(QueryDepositRequest request)` => Deposit queries single deposit information based proposalID, depositAddr. +- `Deposits(QueryDepositsRequest request)` => Deposits queries all deposits of a single proposal. +- `Params(QueryParamsRequest request)` => Params queries all parameters of the gov module. +- `Proposal(QueryProposalRequest request)` => Proposal queries proposal details based on ProposalID. +- `Proposals(QueryProposalsRequest request)` => Proposals queries all proposals based on given status. +- `TallyResult(QueryTallyResultRequest request)` => TallyResult queries the tally of a proposal vote. +- `Vote(QueryVoteRequest request)` => Vote queries voted information based on proposalID, voterAddr. +- `Votes(QueryVotesRequest request)` => Votes queries votes of a given proposal. + +#### [secretClient.Query.IbcChannel](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.IbcChannelQueryClient.htm) +- `Channel(QueryChannelRequest request)` => Channel queries an IBC Channel. +- `Channels(QueryChannelsRequest request)` => Channels queries all the IBC channels of a chain. +- `ChannelClientState(QueryChannelClientStateRequest request)` => ChannelClientState queries for the client state for the channel associated with the provided channel identifiers. +- `ChannelConsensusState(QueryChannelConsensusStateRequest request)` => ChannelConsensusState queries for the consensus state for the channel associated with the provided channel identifiers. +- `ConnectionChannels(QueryConnectionChannelsRequest request)` => ConnectionChannels queries all the channels associated with a connection end. +- `NextSequenceReceive(QueryNextSequenceReceiveRequest request)` => NextSequenceReceive returns the next receive sequence for a given channel. +- `PacketAcknowledgement(QueryPacketAcknowledgementRequest request)` => PacketAcknowledgement queries a stored packet acknowledgement hash. +- `PacketAcknowledgements(QueryPacketAcknowledgementsRequest request)` => PacketAcknowledgements returns all the packet acknowledgements associated with a channel. +- `PacketCommitment(QueryPacketCommitmentRequest request)` => PacketCommitment queries a stored packet commitment hash. +- `PacketCommitments(QueryPacketCommitmentsRequest request)` => PacketCommitments returns all the packet commitments hashes associated with a channel. +- `PacketReceipt(QueryPacketReceiptRequest request)` => PacketReceipt queries if a given packet sequence has been received on the queried chain. +- `UnreceivedAcks(QueryUnreceivedAcksRequest request)` => UnreceivedAcks returns all the unreceived IBC acknowledgements associated with a channel and sequences. +- `UnreceivedPackets(QueryUnreceivedPacketsRequest request)` => UnreceivedPackets returns all the unreceived IBC packets associated with a channel and sequences. + +#### [secretClient.Query.IbcClient](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.IbcClientQueryClient.htm) +- `ClientParams(QueryClientParamsRequest request)` => ClientParams queries all parameters of the ibc client. +- `ClientState(QueryClientStateRequest request)` => ClientState queries an IBC light client. +- `ClientStates(QueryClientStatesRequest request)` => ClientStates queries all the IBC light clients of a chain. +- `ClientStatus(QueryClientStatusRequest request)` => Status queries the status of an IBC client. +- `ConsensusState(QueryConsensusStateRequest request)` => ConsensusState queries a consensus state associated with a client state at a given height. +- `ConsensusStates(QueryTallyResultRequest request)` => ConsensusStates queries all the consensus state associated with a given client. +- `UpgradedClientState(QueryUpgradedClientStateRequest request)` => UpgradedClientState queries an Upgraded IBC light client. +- `UpgradedConsensusState(QueryUpgradedConsensusStateRequest request)` => UpgradedConsensusState queries an Upgraded IBC consensus state. + +#### [secretClient.Query.IbcConnection](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.IbcConnectionQueryClient.htm) +- `ClientConnections(QueryClientConnectionsRequest request)` => ClientConnections queries the connection paths associated with a client state. +- `Connection(QueryConnectionRequest request)` => Connection queries an IBC connection end. +- `Connections(QueryConnectionsRequest request)` => Connections queries all the IBC connections of a chain. +- `ConnectionClientState(QueryConnectionClientStateRequest request)` => ConnectionClientState queries the client state associated with the connection. +- `ConnectionConsensusState(QueryConnectionConsensusStateRequest request)` => ConnectionConsensusState queries the consensus state associated with the connection. + +#### [secretClient.Query.IbcTransfer](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.IbcTransferQueryClient.htm) +- `DenomHash(QueryDenomHashRequest request)` => DenomHash queries a denomination hash information. +- `DenomTrace(QueryDenomTraceRequest request)` => DenomTrace queries a denomination trace information. +- `DenomTraces(QueryDenomTracesRequest request)` => DenomTraces queries all denomination traces. +- `Params(QueryParamsRequest request)` => Params queries all parameters of the ibc-transfer module. + +#### [secretClient.Query.Mint](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.MintQueryClient.htm) +- `AnnualProvisions(QueryAnnualProvisionsRequest request)` => AnnualProvisions current minting annual provisions value. +- `Inflation(QueryInflationRequest request)` => Inflation returns the current minting inflation value. +- `Params(QueryParamsRequest request)` => Params returns the total set of minting parameters. + +#### [secretClient.Query.Params](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.ParamsQueryClient.htm) +- `Params(QueryParamsRequest request)` => Params queries a specific parameter of a module, given its subspace and key. + +#### [secretClient.Query.Registration](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.RegistrationQueryClient.htm) +- `EncryptedSeed(QueryEncryptedSeedRequest request)` => Encrypteds the seed. +- `RegistrationKey()` => Returns the key used for registration. +- `TxKey()` => Returns the key used for transactions. + +#### [secretClient.Query.Slashing](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.SlashingQueryClient.htm) +- `Params(QueryParamsRequest request)` => Params queries the parameters of slashing module. +- `SigningInfo(QuerySigningInfoRequest request)` => SigningInfo queries the signing info of given cons address. +- `SigningInfos(QuerySigningInfosRequest request)` => SigningInfos queries signing info of all validators. + +#### [secretClient.Query.Staking](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.StakingQueryClient.htm) +- `Delegation(QueryDelegationRequest request)` => Delegation queries delegate info for given validator delegator pair. +- `DelegatorDelegations(QueryDelegatorDelegationsRequest request)` => DelegatorDelegations queries all delegations of a given delegator address. +- `DelegatorValidator(QueryDelegatorValidatorRequest request)` => DelegatorValidator queries validator info for given delegator validator pair. +- `DelegatorsValidators(QueryDelegatorValidatorsRequest request)` => DelegatorValidators queries all validators info for given delegator address. +- `DelegatorUnbondingDelegations(QueryDelegatorUnbondingDelegationsRequest request)` => DelegatorUnbondingDelegations queries all unbonding delegations of a given delegator address. +- `HistoricalInfo(QueryHistoricalInfoRequest request)` => HistoricalInfo queries the historical info for given height. +- `Params(QueryParamsRequest request)` => Parameters queries the staking parameters. +- `Pool(QueryPoolRequest request)` => Pool queries the pool info. +- `Redelegations(QueryRedelegationsRequest request)` => Redelegations queries redelegations of given address. +- `UnbondingDelegation(QueryUnbondingDelegationRequest request)` => UnbondingDelegation queries unbonding info for given validator delegator pair. +- `Validator(QueryValidatorRequest request)` => Validator queries validator info for given validator address. +- `Validators(QueryValidatorsRequest request)` => Validators queries all validators that match the given status. +- `ValidatorDelegations(QueryValidatorDelegationsRequest request)` => ValidatorDelegations queries delegate info for given validator. +- `ValidatorUnbondingDelegations(QueryValidatorUnbondingDelegationsRequest request)` => ValidatorUnbondingDelegations queries unbonding delegations of a validator. + +#### [secretClient.Query.Tendermint](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.TendermintQueryClient.htm) +- `GetBlockByHeight(GetBlockByHeightRequest request)` => GetBlockByHeight queries block for given height. +- `GetLatestBlock(GetLatestBlockRequest request)` => GetLatestBlock returns the latest block. +- `GetLatestValidatorSet(GetLatestValidatorSetRequest request)` => GetLatestValidatorSet queries latest validator-set. +- `GetNodeInfo(GetNodeInfoRequest request)` => GetNodeInfo queries the current node info. +- `GetSyncing(GetSyncingRequest request)` => GetSyncing queries node syncing. +- `GetValidatorSetByHeight(GetValidatorSetByHeightRequest request)` => GetValidatorSetByHeight queries validator-set at a given height. + +#### [secretClient.Query.Upgrade](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.UpgradeQueryClient.htm) +- `AppliedPlan(QueryAppliedPlanRequest request)` => AppliedPlan queries a previously applied upgrade plan by its name. +- `CurrentPlan(QueryCurrentPlanRequest request)` => CurrentPlan queries the current upgrade plan. +- `ModuleVersions(QueryModuleVersionsRequest request)` => ModuleVersions queries the list of module versions from state. Since: cosmos-sdk 0.43. + ## Broadcasting Transactions ## Uploading and initialize Smart Contract ## Calling a Smart Contract From e278d01af11bf1e186e09296e7c73a9945eb0d29 Mon Sep 17 00:00:00 2001 From: Codemonkey <107538234+0xxCodemonkey@users.noreply.github.com> Date: Tue, 20 Sep 2022 16:11:27 +0200 Subject: [PATCH 08/31] Update README.md --- README.md | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index dfb2bc0b..da825295 100644 --- a/README.md +++ b/README.md @@ -160,10 +160,11 @@ Later via prop: ``` csharp secretNetworkClient.Wallet = walletFromMnemonic; ``` -# Querier (`secretClient.Query`) -The querier can only send queries and get chain information. Access to all query types can be done via ```SecretNetworkClient.Query```. +# SecretNetworkClient +[**Full API »**](https://0xxcodemonkey.github.io/SecretNET/html/T-SecretNET.SecretNetworkClient.htm) -## Sending Queries +## Querier (`secretClient.Query`) +The querier can only send queries and get chain information. Access to all query types can be done via ```SecretNetworkClient.Query```. ### `secretClient.Query.GetTx(string hash, bool tryToDecrypt = true)` Returns a transaction with a txhash. `hash` is a 64 character upper-case hex string. @@ -398,16 +399,14 @@ See all details in the [**Full API »**](https://0xxcodemonkey.github.io/SecretN - `CurrentPlan(QueryCurrentPlanRequest request)` => CurrentPlan queries the current upgrade plan. - `ModuleVersions(QueryModuleVersionsRequest request)` => ModuleVersions queries the list of module versions from state. Since: cosmos-sdk 0.43. -## Broadcasting Transactions -## Uploading and initialize Smart Contract -## Calling a Smart Contract -## Interacting with an Token Contract (SNIP20) -## Interacting with an NFT Contract (SNIP721) -# SecretNetworkClient -[**Full API »**](https://0xxcodemonkey.github.io/SecretNET/html/T-SecretNET.SecretNetworkClient.htm) - -## Querier -The querier can only send queries and get chain information. Access to all query types can be done via ```SecretNetworkClient.Query```. - ## Transactions Use ```SecretNetworkClient.Tx``` to broadcast transactions. + +### Broadcasting transactions +### Uploading and initialize smart contract +### Calling a smart contract +### Interacting with an token contract (SNIP20) +### Interacting with an NFT contract (SNIP721) + +### All transactions (eg. ) + From 900ef66a54a283aab9b38318f45fc76f03814dc9 Mon Sep 17 00:00:00 2001 From: Codemonkey <107538234+0xxCodemonkey@users.noreply.github.com> Date: Tue, 20 Sep 2022 16:12:54 +0200 Subject: [PATCH 09/31] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index da825295..7c833520 100644 --- a/README.md +++ b/README.md @@ -218,7 +218,7 @@ var queryContractResult = await secretClient.Query.Compute.QueryContract Console.WriteLine(queryContractResult.Response); // JSON string ``` -Or even easier for a **SNIP20 Contract** via **SecretNET.SNIP20** Add-On: +Or even easier for a **SNIP20 Contract** via [**SecretNET.SNIP20**](#additional-packages) Add-On: ```csharp var snip20Client = new SecretNET.SNIP20.Snip20Client(secretClient); From dee1c51abc50d18f49bd4d7636086c34adabad72 Mon Sep 17 00:00:00 2001 From: Codemonkey <107538234+0xxCodemonkey@users.noreply.github.com> Date: Tue, 20 Sep 2022 16:14:37 +0200 Subject: [PATCH 10/31] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7c833520..6f77e764 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ You can find the **full API-documentation** here => [https://0xxcodemonkey.githu - [Import via Keplr QR](#import-via-keplr-qr) - [Generating a new account](#generating-a-new-account) - [Attaching the wallet to the SecretNetworkClient (required for signing transactions)](#attaching-the-wallet-to-the-secretnetworkclient-required-for-signing-transactions) - - [Sending Queries](#sending-queries) + - [Sending Queries](#querier-secretclientquery) - [Broadcasting Transactions](#broadcasting-transactions) - [Uploading and initialize Smart Contract](#uploading-and-initialize-smart-contract) - [Calling a Smart Contract](#calling-a-smart-contract) From 6b78a65ca6ab2bb50d6a772a8917c9436448c147 Mon Sep 17 00:00:00 2001 From: 0xxCodemonkey Date: Tue, 20 Sep 2022 16:15:02 +0200 Subject: [PATCH 11/31] Fix method names --- src/Query/MintQueryClient.cs | 2 +- src/Query/UpgradeQueryClient.cs | 2 +- src/SecretNetworkClient.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Query/MintQueryClient.cs b/src/Query/MintQueryClient.cs index 1f0ebce0..3447b53e 100644 --- a/src/Query/MintQueryClient.cs +++ b/src/Query/MintQueryClient.cs @@ -34,7 +34,7 @@ private Cosmos.Mint.V1Beta1.Query.QueryClient client /// /// The request. /// QueryParamsResponse. - public async Task DenomTrace(QueryParamsRequest request) + public async Task Params(QueryParamsRequest request) { var result = await client.ParamsAsync(request); return result; diff --git a/src/Query/UpgradeQueryClient.cs b/src/Query/UpgradeQueryClient.cs index e94e43a1..f4d50162 100644 --- a/src/Query/UpgradeQueryClient.cs +++ b/src/Query/UpgradeQueryClient.cs @@ -34,7 +34,7 @@ private Cosmos.Upgrade.V1Beta1.Query.QueryClient client /// /// The request. /// QueryCurrentPlanResponse. - public async Task GetNodeInfo(QueryCurrentPlanRequest request) + public async Task CurrentPlan(QueryCurrentPlanRequest request) { var result = await client.CurrentPlanAsync(request); return result; diff --git a/src/SecretNetworkClient.cs b/src/SecretNetworkClient.cs index 80151000..53071801 100644 --- a/src/SecretNetworkClient.cs +++ b/src/SecretNetworkClient.cs @@ -113,7 +113,7 @@ public Queries Query { if (_queries == null) { - _queries = new Queries(this, _grpcChannel, _rpcMessageInterceptor); + _queries = new Queries(this, Tx, _grpcChannel, _rpcMessageInterceptor); } return _queries; } From fe1ef76fac2fdb17a4691c403c9af7ea0e639088 Mon Sep 17 00:00:00 2001 From: Codemonkey <107538234+0xxCodemonkey@users.noreply.github.com> Date: Tue, 20 Sep 2022 16:25:10 +0200 Subject: [PATCH 12/31] Update README.md --- README.md | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 6f77e764..411168b2 100644 --- a/README.md +++ b/README.md @@ -31,24 +31,26 @@ You can find the **full API-documentation** here => [https://0xxcodemonkey.githu - [General information](#general-information) - [Usage](#usage) - [Installation](#installation) - - [Additional packages](#additional-packages) -- [Examples](#examples) - - [Creating / Initializing the Wallet](#creating--initializing-the-wallet) - - [Importing account from mnemonic phrase](#importing-account-from-mnemonic-phrase) - - [Importing private key](#importing-private-key) - - [Use previously saved wallet](#use-previously-saved-wallet) - - [Import via Keplr QR](#import-via-keplr-qr) - - [Generating a new account](#generating-a-new-account) - - [Attaching the wallet to the SecretNetworkClient (required for signing transactions)](#attaching-the-wallet-to-the-secretnetworkclient-required-for-signing-transactions) - - [Sending Queries](#querier-secretclientquery) - - [Broadcasting Transactions](#broadcasting-transactions) - - [Uploading and initialize Smart Contract](#uploading-and-initialize-smart-contract) - - [Calling a Smart Contract](#calling-a-smart-contract) - - [Interacting with an Token Contract (SNIP20)](#interacting-with-an-token-contract-snip20) - - [Interacting with an NFT Contract (SNIP721)](#interacting-with-an-nft-contract-snip721) + - [Additional packages](#additional-packages) +- [Creating / Initializing the Wallet](#creating--initializing-the-wallet) + - [Importing account from mnemonic phrase](#importing-account-from-mnemonic-phrase) + - [Importing private key](#importing-private-key) + - [Use previously saved wallet](#use-previously-saved-wallet) + - [Import via Keplr QR](#import-via-keplr-qr) + - [Generating a new account](#generating-a-new-account) + - [Attaching the wallet to the SecretNetworkClient (required for signing transactions)](#attaching-the-wallet-to-the-secretnetworkclient-required-for-signing-transactions) - [SecretNetworkClient](#secretnetworkclient) - - [Querier](#querier) + - [Queries](#querier-secretclientquery) + - [Get SCRT Balance](#get-scrt-balance) + - [Query smart contract (permissionless method)](#query-smart-contract-permissionless-method) + - [All queries (eg. accounts, bank, compute, gov, feegrant, etc.)](#all-queries-eg-accounts-bank-compute-gov-feegrant-etc) - [Transactions](#transactions) + - [Broadcasting Transactions](#broadcasting-transactions) + - [Uploading and initialize Smart Contract](#uploading-and-initialize-smart-contract) + - [Calling a Smart Contract](#calling-a-smart-contract) + - [Interacting with an Token Contract (SNIP20)](#interacting-with-an-token-contract-snip20) + - [Interacting with an NFT Contract (SNIP721)](#interacting-with-an-nft-contract-snip721) + - [All transactions (eg. )](#all-transactions-eg-) # General information The rough structure of the Secret.NET client, from the user's perspective, is divided into the following areas: From cb1d7ab3ca5f08fcaa20be373636fe63a9248384 Mon Sep 17 00:00:00 2001 From: Codemonkey <107538234+0xxCodemonkey@users.noreply.github.com> Date: Tue, 20 Sep 2022 16:25:37 +0200 Subject: [PATCH 13/31] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 411168b2..6742d4fd 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,6 @@ You can find the **full API-documentation** here => [https://0xxcodemonkey.githu - [Key Features](#key-features) - [Additional packages](#additional-packages) - [Full API-documentation](#full-api-documentation) -- [Table of Contents](#table-of-contents) - [General information](#general-information) - [Usage](#usage) - [Installation](#installation) From b981b1577978ad207ac8af12f3711e7d8d2cce23 Mon Sep 17 00:00:00 2001 From: 0xxCodemonkey Date: Wed, 21 Sep 2022 12:31:14 +0200 Subject: [PATCH 14/31] Update nuget infos and docs --- NuGet_README.md | 7 ++++--- README.md | 3 ++- src/SecretNET.csproj | 9 +++++---- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/NuGet_README.md b/NuGet_README.md index 67bfe2a0..6996c81b 100644 --- a/NuGet_README.md +++ b/NuGet_README.md @@ -1,5 +1,6 @@ # Secret.NET Core Library -Secret.NET (port of the [secret.js](https://github.com/scrtlabs/secret.js) Client) is an .NET SDK for writing applications that interact with the [Secret Network blockchain](https://scrt.network/). +Secret.NET (port of the [secret.js](https://github.com/scrtlabs/secret.js) Client) is a .NET Client to interact with the [Secret Network blockchain](https://scrt.network/) (L1 / Cosmos based), the first privacy smart contract blockchain that processes and stores data on-chain in encrypted form (SGX). +This allows [unique use cases](https://docs.scrt.network/secret-network-documentation/secret-network-overview/use-cases) like Secret NFTs where you can store public and private data e.g., Encryption Keys, passwords or other secrets. # Key Features - Written in .NET 6 including MAUI Support. @@ -11,6 +12,8 @@ Secret.NET (port of the [secret.js](https://github.com/scrtlabs/secret.js) Clien - The SDK has a wallet built in and does not currently require / support external wallets. - Custom APIs / clients for specific smart contracts can be easily created (see packages for tokens / SNIP20 or NFT / SNIP721). +All information and documentation is available in the [**GitHub repository**](https://github.com/0xxCodemonkey/SecretNET). + :information_source: This library is still in beta (as [secret.js](https://github.com/scrtlabs/secret.js)), APIs may break. Beta testers are welcome! ## Additional packages @@ -18,9 +21,7 @@ In addition to the Secret.NET Core Library, the following complementary packages - [**Full SNIP-20 (Token) client**](https://github.com/0xxCodemonkey/SecretNET.SNIP20), which exposes all methods of the [SNIP-20 reference implementation](https://github.com/scrtlabs/snip20-reference-impl). - [**Full SNIP-721 / SNIP-722 (NFT) client**](https://github.com/0xxCodemonkey/SecretNET.SNIP721), which exposes all methods of the [SNIP-721 reference implementation](https://github.com/baedrik/snip721-reference-impl). - ## Links - - [GitHub](https://github.com/0xxCodemonkey/SecretNET) - [Secret Network - Secret Network is the first blockchain with customizable privacy.](https://scrt.network/) - [Secret Network - Documentation](https://docs.scrt.network/secret-network-documentation/) \ No newline at end of file diff --git a/README.md b/README.md index 6742d4fd..1e8da727 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Secret.NET Core Library -Secret.NET (port of the [secret.js](https://github.com/scrtlabs/secret.js) Client) is an .NET SDK for writing applications that interact with the [Secret Network blockchain](https://scrt.network/). +Secret.NET (port of the [secret.js](https://github.com/scrtlabs/secret.js) Client) is a .NET Client to interact with the [Secret Network blockchain](https://scrt.network/) (L1 / Cosmos based), the first privacy smart contract blockchain that processes and stores data on-chain in encrypted form (SGX). +This allows [unique use cases](https://docs.scrt.network/secret-network-documentation/secret-network-overview/use-cases) like Secret NFTs where you can store public and private data e.g., Encryption Keys, passwords or other secrets. # Key Features - Written in .NET 6 including MAUI Support. diff --git a/src/SecretNET.csproj b/src/SecretNET.csproj index e8097f08..6d671b46 100644 --- a/src/SecretNET.csproj +++ b/src/SecretNET.csproj @@ -21,17 +21,17 @@ True True Secret.NET (port of the secret.js Client) is an .NET SDK for writing applications that interact with the Secret Network blockchain. - 0.3.0-alpha + 0.3.0 NuGet_README.md https://github.com/0xxCodemonkey/SecretNET - blockchain;privacy + Secret Network;Blockchain;Privacy;Cosmos;IBC;MAUI; SecretNetwork_Logo.png https://github.com/0xxCodemonkey/SecretNET - Secret.NET (port of the secret.js Client) is an .NET SDK for writing applications that interact with the Secret Network blockchain. + .NET Client to interact with the Secret Network blockchain (L1 / Cosmos), the first privacy smart contract blockchain that processes and stores data on-chain in encrypted form (SGX). This allows unique use cases like NFTs where you can store public and private data e.g., Encryption Keys. This lib supports .NET MAUI and can be used for Android, iOS / Mac. Codemonkey Codemonkey - CosmWasm v1 and adaptation to secretjs v.1.4 changes + Shockwave Delta update (CosmWasm 1.0, IBC and much more) @@ -213,6 +213,7 @@ True \ + True \ From 163f77d0c9aa52728847e97ba3cfdbced9c513ea Mon Sep 17 00:00:00 2001 From: Codemonkey <107538234+0xxCodemonkey@users.noreply.github.com> Date: Wed, 21 Sep 2022 13:02:11 +0200 Subject: [PATCH 15/31] Update README.md --- README.md | 132 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 97 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index 1e8da727..3b662fb6 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ # Secret.NET Core Library -Secret.NET (port of the [secret.js](https://github.com/scrtlabs/secret.js) Client) is a .NET Client to interact with the [Secret Network blockchain](https://scrt.network/) (L1 / Cosmos based), the first privacy smart contract blockchain that processes and stores data on-chain in encrypted form (SGX). -This allows [unique use cases](https://docs.scrt.network/secret-network-documentation/secret-network-overview/use-cases) like Secret NFTs where you can store public and private data e.g., Encryption Keys, passwords or other secrets. +**Secret.NET** (port of the [secret.js](https://github.com/scrtlabs/secret.js) Client) is a .NET Client to interact with the [Secret Network blockchain](https://scrt.network/) (L1 / Cosmos based), the first privacy smart contract blockchain that processes and stores data on-chain in encrypted form (SGX). + +This allows [unique use cases](https://docs.scrt.network/secret-network-documentation/secret-network-overview/use-cases) like **Secret NFTs where you can store public and private data** e.g., encryption keys, passwords or other secrets. # Key Features - Written in .NET 6 including MAUI Support. @@ -29,25 +30,28 @@ You can find the **full API-documentation** here => [https://0xxcodemonkey.githu - [Additional packages](#additional-packages) - [Full API-documentation](#full-api-documentation) - [General information](#general-information) -- [Usage](#usage) - - [Installation](#installation) +- [Installation](#installation) - [Additional packages](#additional-packages) -- [Creating / Initializing the Wallet](#creating--initializing-the-wallet) - - [Importing account from mnemonic phrase](#importing-account-from-mnemonic-phrase) - - [Importing private key](#importing-private-key) - - [Use previously saved wallet](#use-previously-saved-wallet) - - [Import via Keplr QR](#import-via-keplr-qr) - - [Generating a new account](#generating-a-new-account) - - [Attaching the wallet to the SecretNetworkClient (required for signing transactions)](#attaching-the-wallet-to-the-secretnetworkclient-required-for-signing-transactions) -- [SecretNetworkClient](#secretnetworkclient) - - [Queries](#querier-secretclientquery) - - [Get SCRT Balance](#get-scrt-balance) - - [Query smart contract (permissionless method)](#query-smart-contract-permissionless-method) +- [Usage Examples](#usage-examples) + -[Sending Queries](#sending-queries) + - [Get SCRT Balance](#get-scrt-balance) + - [Query smart contract (permissionless method)](#query-smart-contract-permissionless-method) + -[Broadcasting Transactions](#broadcasting-transactions) + - [Calling a Smart Contract](#calling-a-smart-contract) +-[API](#api) + - [Creating / Initializing the Wallet](#creating--initializing-the-wallet) + - [Importing account from mnemonic phrase](#importing-account-from-mnemonic-phrase) + - [Importing private key](#importing-private-key) + - [Use previously saved wallet](#use-previously-saved-wallet) + - [Import via Keplr QR](#import-via-keplr-qr) + - [Generating a new account](#generating-a-new-account) + - [Attaching the wallet to the SecretNetworkClient (required for signing transactions)](#attaching-the-wallet-to-the-secretnetworkclient-required-for-signing-transactions) + - [SecretNetworkClient](#secretnetworkclient) + - [Queries](#querier-secretclientquery) + - [All queries (eg. accounts, bank, compute, gov, feegrant, etc.)](#all-queries-eg-accounts-bank-compute-gov-feegrant-etc) - [Transactions](#transactions) - - [Broadcasting Transactions](#broadcasting-transactions) - - [Uploading and initialize Smart Contract](#uploading-and-initialize-smart-contract) - - [Calling a Smart Contract](#calling-a-smart-contract) + - [Uploading and initialize Smart Contract](#uploading-and-initialize-smart-contract) - [Interacting with an Token Contract (SNIP20)](#interacting-with-an-token-contract-snip20) - [Interacting with an NFT Contract (SNIP721)](#interacting-with-an-nft-contract-snip721) - [All transactions (eg. )](#all-transactions-eg-) @@ -70,8 +74,7 @@ All transactions can also be simulated via ``Tx.Simulate`` to determine the esti ![](resources/VS_IntelliSense.png) -# Usage -## Installation +# Installation The Secret.NET Core Libray can be easily installed via Nuget: ``` nuget.exe ``` -CLI: @@ -97,7 +100,66 @@ Install-Package SecretNET.SNIP20 Install-Package SecretNET.SNIP721 Install-Package SecretNET.UI (coming soon) ``` -# Examples +# Usage Examples +Note: Public gRPC-web endpoints can be found in https://github.com/scrtlabs/api-registry for both mainnet and testnet. + +For a lot more usage examples refer to the tests. + +## Sending Queries +```csharp +using SecretNET; + +// grpcWebUrl => TODO get from https://github.com/scrtlabs/api-registry +// chainId e.g. "secret-4" or "pulsar-2" +var secretClient = new SecretNetworkClient(grpcWebUrl, chainId, wallet: null); // no wallet needed for queries + +var response = await secretClient.Query.Bank.Balance("secret1ap26qrlp8mcq2pg6r47w43l0y8zkqm8a450s03"); +Console.WriteLine("Balance: " + (float.Parse(response.Amount) / 1000000f)); // denom: "uscrt" + +// simple counter contract +var contractAddress = "secret12j8e6aeyy9ff7drfks3knxva0pxj847fle8zsf"; // pulsar-2 +var contractCodeHash = "3d528d0d9889d5887abd3e497243ed6e5a4da008091e20ee420eca39874209ad"; + +var getCountQueryMsg = new { get_count = new { } }; + +var queryContractResult = await secretClient.Query.Compute.QueryContract( + contractAddress: contractAddress, + queryMsg: getCountQueryMsg, + codeHash: contractCodeHash); // optional but way faster + +Console.WriteLine(queryContractResult.Response); // JSON string +``` +## Broadcasting Transactions +```csharp +using SecretNET; + +var myWallet = await SecretNET.Wallet.Create("detect unique diary skate horse hockey gain naive approve rabbit act lift"); + +// grpcWebUrl => TODO get from https://github.com/scrtlabs/api-registry +// chainId e.g. "secret-4" or "pulsar-2" +var secretClient = new SecretNetworkClient(grpcWebUrl, chainId, wallet: myWallet); // wallet needed for transactions + +// simple counter contract +var contractAddress = "secret12j8e6aeyy9ff7drfks3knxva0pxj847fle8zsf"; // pulsar-2 +var contractCodeHash = "3d528d0d9889d5887abd3e497243ed6e5a4da008091e20ee420eca39874209ad"; + +var msgExecuteContract = new SecretNET.Tx.MsgExecuteContract( + contractAddress: contractAddress, + msg: new { increment = new { } }, + codeHash: contractCodeHash); // optional but way faster + +var txOptionsExecute = new TxOptions() +{ + GasLimit = 300_000, + GasPriceInFeeDenom = 0.26F +}; + +var executeContractResult = await secretClient.Tx.Compute.ExecuteContract( + msg: msgExecuteContract, + txOptions: txOptionsExecute); +``` + +# API ## Creating / Initializing the wallet When initializing a wallet, you must pass an ```IPrivateKeyStorage``` provider where the private key and mnemonic phrase will be stored (default = MauiSecureStorage). The following providers are available out of the box: @@ -162,18 +224,18 @@ Later via prop: ``` csharp secretNetworkClient.Wallet = walletFromMnemonic; ``` -# SecretNetworkClient +## SecretNetworkClient [**Full API »**](https://0xxcodemonkey.github.io/SecretNET/html/T-SecretNET.SecretNetworkClient.htm) -## Querier (`secretClient.Query`) +### Querier (`secretClient.Query`) The querier can only send queries and get chain information. Access to all query types can be done via ```SecretNetworkClient.Query```. -### `secretClient.Query.GetTx(string hash, bool tryToDecrypt = true)` +#### `secretClient.Query.GetTx(string hash, bool tryToDecrypt = true)` Returns a transaction with a txhash. `hash` is a 64 character upper-case hex string. If set the parameter tryToDecrypt to true (default) the client tries to decrypt the tx data (works only if the tx was created in the same session / client instance or if the same CreateClientOptions.EncryptionSeed is used). -### `secretClient.Query.TxsQuery(string query, bool tryToDecrypt = false)` +#### `secretClient.Query.TxsQuery(string query, bool tryToDecrypt = false)` Returns all transactions that match a query. If set the parameter `tryToDecrypt` to true (default = false) the client tries to decrypt the tx data (works only if the tx was created in the same session / client instance or if the same ```CreateClientOptions.EncryptionSeed``` is used). @@ -199,14 +261,14 @@ var response = await secretClient.Query.Bank.Balance("secret1ap26qrlp8mcq2pg6r47 Console.WriteLine("Balance: " + (float.Parse(response.Amount) / 1000000f)); // denom: "uscrt" ``` -### Query smart contract (permissionless method) +#### Query smart contract (permissionless method) ```csharp // grpcWebUrl => TODO get from https://github.com/scrtlabs/api-registry // chainId e.g. "secret-4" or "pulsar-2" var secretClient = new SecretNetworkClient(grpcWebUrl, chainId, wallet: null); -// https://github.com/scrtlabs/mysimplecounter +// simple counter contract var contractAddress = "secret12j8e6aeyy9ff7drfks3knxva0pxj847fle8zsf"; // pulsar-2 var contractCodeHash = "3d528d0d9889d5887abd3e497243ed6e5a4da008091e20ee420eca39874209ad"; @@ -235,7 +297,7 @@ var tokenInfoResult = (await snip20Client.Query.GetTokenInfo( Console.WriteLine($"TokenName: {tokenInfoResult.Name}, Symbol: {tokenInfoResult.Symbol}"); ``` -### All queries (eg. accounts, bank, compute, gov, feegrant, etc.) +#### All queries (eg. accounts, bank, compute, gov, feegrant, etc.) - secretClient.Query.Auth - secretClient.Query.Authz - secretClient.Query.Bank @@ -258,16 +320,16 @@ Console.WriteLine($"TokenName: {tokenInfoResult.Name}, Symbol: {tokenInfoResult. See all details in the [**Full API »**](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.Queries.htm) -#### [secretClient.Query.Auth](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.AuthQueryClient.htm) +##### [secretClient.Query.Auth](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.AuthQueryClient.htm) - `Account(string address)` => Returns account details based on address. - `Accounts()` => Returns all existing accounts on the blockchain. -#### [secretClient.Query.Authz](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.AuthzQueryClient.htm) +##### [secretClient.Query.Authz](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.AuthzQueryClient.htm) - `GranteeGrants(QueryGranteeGrantsRequest request)` => GranteeGrants returns a list of `GrantAuthorization` by grantee. Since: cosmos-sdk 0.45.2. - `GranterGrants(QueryGranterGrantsRequest request)` => GranterGrants returns list of `GrantAuthorization`, granted by granter. Since: cosmos-sdk 0.45.2. - `Grants(QueryGrantsRequest request)` => Returns list of `Authorization`, granted to the grantee by the granter. -#### [secretClient.Query.Bank](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.BankQueryClient.htm) +##### [secretClient.Query.Bank](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.BankQueryClient.htm) - `Balance(QueryBalanceRequest request)` / `Balance(string address, string denom)` => Balance queries the balance of **a single** coin for a single account. - `Balance(QueryAllBalancesRequest request)` => AllBalances queries the balance of **all coins** for a single account. - `DenomMetadata(QueryDenomMetadataRequest request)` => DenomsMetadata queries the client metadata of **a given coin** denomination. @@ -277,7 +339,7 @@ See all details in the [**Full API »**](https://0xxcodemonkey.github.io/SecretN - `SupplyOf(QuerySupplyOfRequest request)` => SupplyOf queries the supply of a single coin. - `TotalSupply(QueryTotalSupplyRequest request)` => TotalSupply queries the total supply of all coins. -#### [secretClient.Query.Compute](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.ComputeQueryClient.htm) +##### [secretClient.Query.Compute](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.ComputeQueryClient.htm) - `Code(ulong codeId, Metadata metadata)` => Get WASM bytecode and metadata for a code id. - `Codes(Metadata metadata)` => Query all codes on chain. - `ContractInfo(string contractAddress, Metadata metadata)` => Get metadata of a Secret Contract. @@ -286,7 +348,7 @@ See all details in the [**Full API »**](https://0xxcodemonkey.github.io/SecretN - `GetCodeHashByCodeId(ulong codeId, Metadata metadata)` => Get the codeHash from a code id. - `QueryContract(string contractAddress, Object queryMsg, string codeHash, Metadata metadata)` => Query a Secret Contract and cast the response as `R`. (see above) -#### [secretClient.Query.Distribution](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.DistributionQueryClient.htm) +##### [secretClient.Query.Distribution](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.DistributionQueryClient.htm) - `Balance(QueryParamsRequest request)` => CommunityPool queries the community pool coins. - `CommunityPool(QueryCommunityPoolRequest request)` => Query all codes on chain. - `DelegationRewards(QueryDelegationRewardsRequest request)` => DelegationRewards queries the total rewards accrued by a delegation. @@ -298,11 +360,11 @@ See all details in the [**Full API »**](https://0xxcodemonkey.github.io/SecretN - `ValidatorOutstandingRewards(QueryValidatorCommissionRequest request)` => ValidatorOutstandingRewards queries rewards of a validator address. - `ValidatorSlashes(QueryValidatorCommissionRequest request)` => ValidatorSlashes queries slash events of a validator. -#### [secretClient.Query.Evidence](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.EvidenceQueryClient.htm) +##### [secretClient.Query.Evidence](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.EvidenceQueryClient.htm) - `AllEvidence(QueryAllEvidenceRequest request)` => AllEvidence queries all evidence. - `FoundationTax(QueryEvidenceRequest request)` => Evidence queries evidence based on evidence hash. -#### [secretClient.Query.Feegrant](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.FeegrantQueryClient.htm) +##### [secretClient.Query.Feegrant](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.FeegrantQueryClient.htm) - `Allowance(QueryAllowanceRequest request)` => Allowance returns fee granted to the grantee by the granter. - `Allowances(QueryAllowancesRequest request)` => Allowances returns all the grants for address. From 8546829ec9c3e37e813737ebf248e104cc0f57ab Mon Sep 17 00:00:00 2001 From: Codemonkey <107538234+0xxCodemonkey@users.noreply.github.com> Date: Wed, 21 Sep 2022 13:05:51 +0200 Subject: [PATCH 16/31] Update README.md --- README.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 3b662fb6..a22da8b8 100644 --- a/README.md +++ b/README.md @@ -33,12 +33,9 @@ You can find the **full API-documentation** here => [https://0xxcodemonkey.githu - [Installation](#installation) - [Additional packages](#additional-packages) - [Usage Examples](#usage-examples) - -[Sending Queries](#sending-queries) - - [Get SCRT Balance](#get-scrt-balance) - - [Query smart contract (permissionless method)](#query-smart-contract-permissionless-method) - -[Broadcasting Transactions](#broadcasting-transactions) - - [Calling a Smart Contract](#calling-a-smart-contract) --[API](#api) + - [Sending Queries](#sending-queries) + - [Broadcasting Transactions](#broadcasting-transactions) +- [API](#api) - [Creating / Initializing the Wallet](#creating--initializing-the-wallet) - [Importing account from mnemonic phrase](#importing-account-from-mnemonic-phrase) - [Importing private key](#importing-private-key) From bd912e35def360c8308e87a62576ac0286051282 Mon Sep 17 00:00:00 2001 From: Codemonkey <107538234+0xxCodemonkey@users.noreply.github.com> Date: Wed, 21 Sep 2022 13:08:41 +0200 Subject: [PATCH 17/31] Update README.md --- README.md | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index a22da8b8..12a6ab76 100644 --- a/README.md +++ b/README.md @@ -36,16 +36,15 @@ You can find the **full API-documentation** here => [https://0xxcodemonkey.githu - [Sending Queries](#sending-queries) - [Broadcasting Transactions](#broadcasting-transactions) - [API](#api) - - [Creating / Initializing the Wallet](#creating--initializing-the-wallet) - - [Importing account from mnemonic phrase](#importing-account-from-mnemonic-phrase) - - [Importing private key](#importing-private-key) - - [Use previously saved wallet](#use-previously-saved-wallet) - - [Import via Keplr QR](#import-via-keplr-qr) - - [Generating a new account](#generating-a-new-account) - - [Attaching the wallet to the SecretNetworkClient (required for signing transactions)](#attaching-the-wallet-to-the-secretnetworkclient-required-for-signing-transactions) - - [SecretNetworkClient](#secretnetworkclient) - - [Queries](#querier-secretclientquery) - + - [Creating / Initializing the Wallet](#creating--initializing-the-wallet) + - [Importing account from mnemonic phrase](#importing-account-from-mnemonic-phrase) + - [Importing private key](#importing-private-key) + - [Use previously saved wallet](#use-previously-saved-wallet) + - [Import via Keplr QR](#import-via-keplr-qr) + - [Generating a new account](#generating-a-new-account) + - [Attaching the wallet to the SecretNetworkClient (required for signing transactions)](#attaching-the-wallet-to-the-secretnetworkclient-required-for-signing-transactions) + - [SecretNetworkClient](#secretnetworkclient) + - [Queries](#querier-secretclientquery) - [All queries (eg. accounts, bank, compute, gov, feegrant, etc.)](#all-queries-eg-accounts-bank-compute-gov-feegrant-etc) - [Transactions](#transactions) - [Uploading and initialize Smart Contract](#uploading-and-initialize-smart-contract) From a829e726e563fee8d2c39a8b6e3ebbc4835f3f1b Mon Sep 17 00:00:00 2001 From: Codemonkey <107538234+0xxCodemonkey@users.noreply.github.com> Date: Wed, 21 Sep 2022 14:46:39 +0200 Subject: [PATCH 18/31] Update README.md --- README.md | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 12a6ab76..400572c6 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ You can find the **full API-documentation** here => [https://0xxcodemonkey.githu - [Importing account from mnemonic phrase](#importing-account-from-mnemonic-phrase) - [Importing private key](#importing-private-key) - [Use previously saved wallet](#use-previously-saved-wallet) - - [Import via Keplr QR](#import-via-keplr-qr) + - Import via Keplr QR (coming soon...) - [Generating a new account](#generating-a-new-account) - [Attaching the wallet to the SecretNetworkClient (required for signing transactions)](#attaching-the-wallet-to-the-secretnetworkclient-required-for-signing-transactions) - [SecretNetworkClient](#secretnetworkclient) @@ -293,6 +293,28 @@ var tokenInfoResult = (await snip20Client.Query.GetTokenInfo( Console.WriteLine($"TokenName: {tokenInfoResult.Name}, Symbol: {tokenInfoResult.Symbol}"); ``` + +## Transactions +On a signer Secret.NET client, `SecretNetworkClient.Tx` is used to broadcast transactions. Every function under secretjs.tx can receive an optional TxOptions. + +### Broadcasting transactions +Used to send a complex transactions, which contains a list of messages. The messages are executed in sequence, and the transaction succeeds if all messages succeed. + +```csharp + +``` + +### Uploading and initialize smart contract +### Calling a smart contract +### Interacting with an token contract (SNIP20) +### Interacting with an NFT contract (SNIP721) + + + + + + + #### All queries (eg. accounts, bank, compute, gov, feegrant, etc.) - secretClient.Query.Auth - secretClient.Query.Authz @@ -459,14 +481,7 @@ See all details in the [**Full API »**](https://0xxcodemonkey.github.io/SecretN - `CurrentPlan(QueryCurrentPlanRequest request)` => CurrentPlan queries the current upgrade plan. - `ModuleVersions(QueryModuleVersionsRequest request)` => ModuleVersions queries the list of module versions from state. Since: cosmos-sdk 0.43. -## Transactions -Use ```SecretNetworkClient.Tx``` to broadcast transactions. -### Broadcasting transactions -### Uploading and initialize smart contract -### Calling a smart contract -### Interacting with an token contract (SNIP20) -### Interacting with an NFT contract (SNIP721) ### All transactions (eg. ) From c7a39e8c310ed8f25fa4d5ec2ad160e63f30a225 Mon Sep 17 00:00:00 2001 From: Codemonkey <107538234+0xxCodemonkey@users.noreply.github.com> Date: Wed, 21 Sep 2022 14:49:21 +0200 Subject: [PATCH 19/31] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 400572c6..3ff3f2ee 100644 --- a/README.md +++ b/README.md @@ -295,7 +295,7 @@ Console.WriteLine($"TokenName: {tokenInfoResult.Name}, Symbol: {tokenInfoResult. ``` ## Transactions -On a signer Secret.NET client, `SecretNetworkClient.Tx` is used to broadcast transactions. Every function under secretjs.tx can receive an optional TxOptions. +On a signer Secret.NET client, `SecretNetworkClient.Tx` is used to broadcast transactions. Every function under `SecretNetworkClient.Tx` can receive an optional TxOptions. ### Broadcasting transactions Used to send a complex transactions, which contains a list of messages. The messages are executed in sequence, and the transaction succeeds if all messages succeed. From e0101654ab7c3d9a0f7d1edc2dde677781ea6099 Mon Sep 17 00:00:00 2001 From: Codemonkey <107538234+0xxCodemonkey@users.noreply.github.com> Date: Wed, 21 Sep 2022 14:49:57 +0200 Subject: [PATCH 20/31] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3ff3f2ee..cac95f4c 100644 --- a/README.md +++ b/README.md @@ -295,7 +295,7 @@ Console.WriteLine($"TokenName: {tokenInfoResult.Name}, Symbol: {tokenInfoResult. ``` ## Transactions -On a signer Secret.NET client, `SecretNetworkClient.Tx` is used to broadcast transactions. Every function under `SecretNetworkClient.Tx` can receive an optional TxOptions. +On a signer Secret.NET client, `SecretNetworkClient.Tx` is used to broadcast transactions. Every function under `SecretNetworkClient.Tx` can receive an optional `TxOptions`. ### Broadcasting transactions Used to send a complex transactions, which contains a list of messages. The messages are executed in sequence, and the transaction succeeds if all messages succeed. From a8333a2d8d8f74ba6f274005ede7bc1603bc6216 Mon Sep 17 00:00:00 2001 From: 0xxCodemonkey Date: Thu, 22 Sep 2022 17:41:33 +0200 Subject: [PATCH 21/31] Restructure Tx and add Tx clients --- src/Query/Queries.cs | 315 ++++++++++++++++++- src/SecretNetworkClient.cs | 24 +- src/Tx/Authz.Msg.cs | 24 +- src/Tx/AuthzTx .Simulate.cs | 49 --- src/Tx/AuthzTx.cs | 22 +- src/Tx/Bank.Msg.cs | 8 +- src/Tx/BankTx.Simulate.cs | 34 -- src/Tx/BankTx.cs | 41 ++- src/Tx/Compute.Msg.cs | 60 +--- src/Tx/ComputeTx.Simulate.cs | 78 ----- src/Tx/ComputeTx.cs | 20 +- src/Tx/Crisis.Msg.cs | 4 +- src/Tx/CrisisTx.Simulate.cs | 24 -- src/Tx/CrisisTx.cs | 12 +- src/Tx/Distribution.Msg.cs | 16 +- src/Tx/DistributionTx.Simulate.cs | 57 ---- src/Tx/DistributionTx.cs | 28 +- src/Tx/Evidence.Msg.cs | 4 +- src/Tx/EvidenceTx.Simulate.cs | 25 -- src/Tx/EvidenceTx.cs | 12 +- src/Tx/Feegrant.Msg.cs | 8 +- src/Tx/FeegrantTx.Simulate.cs | 35 --- src/Tx/FeegrantTx.cs | 17 +- src/Tx/Gov.Msg.cs | 16 +- src/Tx/GovTx.Simulate.cs | 57 ---- src/Tx/GovTx.cs | 27 +- src/Tx/IBC_Channel.Msg.cs | 40 +-- src/Tx/IBC_ChannelTx.cs | 73 +++++ src/Tx/IBC_Client.Msg.cs | 12 +- src/Tx/IBC_ClientTx.cs | 38 +++ src/Tx/IBC_Connection.Msg.cs | 8 +- src/Tx/IBC_ConnectionTx.cs | 38 +++ src/Tx/IBC_Transfer.Msg.cs | 4 +- src/Tx/IBC_TransferTx.Simulate.cs | 26 -- src/Tx/IBC_TransferTx.cs | 12 +- src/Tx/Msg.cs | 54 +++- src/Tx/MsgDecoderRegistry.cs | 96 +++++- src/Tx/Registration.Msg.cs | 4 +- src/Tx/SecretTx.cs | 48 ++- src/Tx/Slashing.Msg.cs | 4 +- src/Tx/SlashingTx.Simulate.cs | 24 -- src/Tx/SlashingTx.cs | 12 +- src/Tx/Staking.Msg.cs | 20 +- src/Tx/StakingTx.Simulate.cs | 68 ---- src/Tx/StakingTx.cs | 32 +- src/Tx/TxClient.cs | 500 +++++++++++------------------- src/Tx/Vesting.Msg.cs | 4 +- src/Tx/VestingTx.Simulate.cs | 24 -- src/Tx/VestingTx.cs | 12 +- 49 files changed, 1041 insertions(+), 1129 deletions(-) delete mode 100644 src/Tx/AuthzTx .Simulate.cs delete mode 100644 src/Tx/BankTx.Simulate.cs delete mode 100644 src/Tx/ComputeTx.Simulate.cs delete mode 100644 src/Tx/CrisisTx.Simulate.cs delete mode 100644 src/Tx/DistributionTx.Simulate.cs delete mode 100644 src/Tx/EvidenceTx.Simulate.cs delete mode 100644 src/Tx/FeegrantTx.Simulate.cs delete mode 100644 src/Tx/GovTx.Simulate.cs create mode 100644 src/Tx/IBC_ChannelTx.cs create mode 100644 src/Tx/IBC_ClientTx.cs create mode 100644 src/Tx/IBC_ConnectionTx.cs delete mode 100644 src/Tx/IBC_TransferTx.Simulate.cs delete mode 100644 src/Tx/SlashingTx.Simulate.cs delete mode 100644 src/Tx/StakingTx.Simulate.cs delete mode 100644 src/Tx/VestingTx.Simulate.cs diff --git a/src/Query/Queries.cs b/src/Query/Queries.cs index cade4b63..d91491d0 100644 --- a/src/Query/Queries.cs +++ b/src/Query/Queries.cs @@ -1,10 +1,16 @@ -namespace SecretNET.Query; +using Cosmos.Base.Abci.V1Beta1; +using SecretNET.Tx; +using System.Collections.Concurrent; +using System.Text.RegularExpressions; + +namespace SecretNET.Query; public class Queries : GprcBase { - private Tx.TxClient _txClient; + private Service.ServiceClient _txClient; + private readonly Regex _errorMessageRegEx = new Regex("; message index: (?\\d+):(?: dispatch: submessages:)* encrypted: (?.+?): (?:instantiate|execute|query) contract failed", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline); - // cosmos + // Cosmos private AuthQueryClient _authQuery; private AuthzQueryClient _authzQuery; private BankQueryClient _bankQuery; @@ -25,7 +31,7 @@ public class Queries : GprcBase private IbcConnectionQueryClient _ibcConnectionQuery; private IbcTransferQueryClient _ibcTransferQuery; - // secret Network + // Secret Network private ComputeQueryClient _computeQuery; private RegistrationQueryClient _registrationQuery; @@ -33,12 +39,10 @@ public class Queries : GprcBase /// Initializes a new instance of the class. /// /// The secret network client. - /// The tx client. /// The GRPC channel. /// The GRPC message interceptor. - internal Queries(ISecretNetworkClient secretNetworkClient, Tx.TxClient txClient, GrpcChannel grpcChannel, CallInvoker? grpcMessageInterceptor) : base(secretNetworkClient, grpcChannel, grpcMessageInterceptor) + internal Queries(ISecretNetworkClient secretNetworkClient, GrpcChannel grpcChannel, CallInvoker? grpcMessageInterceptor) : base(secretNetworkClient, grpcChannel, grpcMessageInterceptor) { - _txClient = txClient; _authQuery = new AuthQueryClient(secretNetworkClient, grpcChannel, grpcMessageInterceptor); _authzQuery = new AuthzQueryClient(secretNetworkClient, grpcChannel, grpcMessageInterceptor); _bankQuery = new BankQueryClient(secretNetworkClient, grpcChannel, grpcMessageInterceptor); @@ -60,26 +64,48 @@ internal Queries(ISecretNetworkClient secretNetworkClient, Tx.TxClient txClient, _upgradeQuery = new UpgradeQueryClient(secretNetworkClient, grpcChannel, grpcMessageInterceptor); } + internal Service.ServiceClient ServiceClient + { + get + { + if (_txClient == null) + { + _txClient = grpcMessageInterceptor != null ? new Service.ServiceClient(grpcMessageInterceptor) : new Service.ServiceClient(grpcChannel); + } + return _txClient; + } + } + /// /// Gets the tx. /// /// The hash. /// if set to true the client tries to decrypt the tx data (works only if the tx was created in the same session / client instance or if the same CreateClientOptions.EncryptionSeed is used). /// SecretTx. - public async Task GetTx(string hash, bool tryToDecrypt = true) + public async Task GetTx(string hash, bool tryToDecrypt = true) { - return await _txClient.GetTx(hash, tryToDecrypt); + var result = await TxsQuery($"tx.hash='{hash}'", tryToDecrypt); + return result?[0]; } + /// /// TXSs the query. /// /// The query. /// if set to true the client tries to decrypt the tx data (works only if the tx was created in the same session / client instance or if the same CreateClientOptions.EncryptionSeed is used). /// SecretTx[]. - public async Task TxsQuery(string query, bool tryToDecrypt = false) + public async Task TxsQuery(string query, bool tryToDecrypt = false) { - return await _txClient.TxsQuery(query, tryToDecrypt); + if (!String.IsNullOrWhiteSpace(query)) + { + GetTxsEventRequest request = new GetTxsEventRequest(); + request.Events.Add(query.Split(" AND ").Select(q => q.Trim()).ToList()); + + var result = await GetTxsEvent(request, tryToDecrypt); + return result; + } + return null; } /// @@ -88,12 +114,13 @@ internal Queries(ISecretNetworkClient secretNetworkClient, Tx.TxClient txClient, /// The request. /// if set to true the client tries to decrypt the tx data (works only if the tx was created in the same session / client instance or if the same CreateClientOptions.EncryptionSeed is used). /// GetTxsEventResponse. - public async Task GetTxsEvent(GetTxsEventRequest request, bool tryToDecrypt = false) + public async Task GetTxsEvent(GetTxsEventRequest request, bool tryToDecrypt = false) { - return await _txClient.GetTxsEvent(request, tryToDecrypt); - } - + var result = await ServiceClient.GetTxsEventAsync(request); + var decodedResponses = await this.DecodeTxResponses(result?.TxResponses?.ToArray(), tryToDecrypt); + return decodedResponses; + } /// /// AuthQuerier is the query interface for the x/auth module @@ -189,4 +216,262 @@ public UpgradeQueryClient Upgrade get { return _upgradeQuery; } } + // internal + + internal async Task DecodeTxResponse(TxResponse txResponse, bool tryToDecrypt = false) + { + if (txResponse == null) return null; + + var result = await DecodeTxResponses(new TxResponse[] { txResponse }, tryToDecrypt); + return result?[0]; + } + + internal async Task DecodeTxResponses(TxResponse[] txResponses, bool tryToDecrypt = false) + { + if (txResponses == null || txResponses.Length == 0) return null; + var result = new ConcurrentBag(); + + await Parallel.ForEachAsync(txResponses, async (txResponse, cancellationToken) => + { + var decodedTx = txResponse.Tx.Unpack(); + + var nonces = new ComputeMsgToNonce(); + + // Decoded input tx messages + for (var i = 0; i < decodedTx?.Body?.Messages?.Count; i++) + { + var rawMsg = decodedTx.Body.Messages[i]; + + var messageDecoder = MsgDecoderRegistry.Get(rawMsg.TypeUrl); + if (messageDecoder == null) continue; + + var msg = messageDecoder(rawMsg); + + if (tryToDecrypt) + { + // Check if the message needs decryption + byte[] inputMsgBytes = null; + if (rawMsg.TypeUrl.IsProtoType(MsgGrantAuthorization.MsgInstantiateContract)) + { + inputMsgBytes = ((Secret.Compute.V1Beta1.MsgInstantiateContract)msg).InitMsg.Span.ToArray(); + } + else if (rawMsg.TypeUrl.IsProtoType(MsgGrantAuthorization.MsgExecuteContract)) + { + inputMsgBytes = ((Secret.Compute.V1Beta1.MsgExecuteContract)msg).Msg.Span.ToArray(); + } + + if (inputMsgBytes != null && inputMsgBytes.Length > 0) + { + // Encrypted, try to decrypt + try + { + var nonce = new ArraySegment(inputMsgBytes, 0, 32).ToArray(); + //Console.WriteLine("DecodeTxResponses nonce: " + nonce.ToHexString()); + + var accountPubkey = new ArraySegment(inputMsgBytes, 32, 32).ToArray(); // unused in decryption + var ciphertext = new ArraySegment(inputMsgBytes, 64, inputMsgBytes.Length - 64).ToArray(); + var plaintext = await Encryption.Decrypt(ciphertext, nonce); + + if (rawMsg.TypeUrl.IsProtoType(MsgGrantAuthorization.MsgInstantiateContract)) + { + ((Secret.Compute.V1Beta1.MsgInstantiateContract)msg).InitMsg = ByteString.CopyFrom(new ArraySegment(plaintext, 64, plaintext.Length - 64).ToArray()); + } + else if (rawMsg.TypeUrl.IsProtoType(MsgGrantAuthorization.MsgExecuteContract)) + { + ((Secret.Compute.V1Beta1.MsgExecuteContract)msg).Msg = ByteString.CopyFrom(new ArraySegment(plaintext, 64, plaintext.Length - 64).ToArray()); + } + + nonces[i] = nonce; // Fill nonces array to later use it in output decryption + + } + catch (Exception ex) + { + // Not encrypted or can't decrypt because not original sender + } + } + } + + decodedTx.Body.Messages[i] = Any.Pack(msg); + } + + txResponse.Tx = Any.Pack(decodedTx); + + var secretTx = new SecretTx(txResponse); + + Func getNounce = (msgIndex) => + { + if (msgIndex < nonces.Count()) + { + var nonce = nonces[msgIndex]; + if (nonce != null && nonce.Length == 32) + { + return nonce; + } + } + return null; + }; + + var tx = txResponse; + List jsonLogs = null; + var arrayLog = new List(); + var rawLog = (string)tx.RawLog.Clone(); + + if (tx.Code == 0 && !string.IsNullOrEmpty(tx.RawLog)) + { + jsonLogs = JsonConvert.DeserializeObject>(tx.RawLog); + + for (int i = 0; i < jsonLogs.Count(); i++) + { + var log = jsonLogs[i]; + if (!log.MsgIndex.HasValue) + { + log.MsgIndex = i; + } + + foreach (var ev in log.Events) + { + foreach (var attr in ev.Attributes) + { + // Try to decrypt + if (tryToDecrypt && ev.MessageType.Equals("wasm", StringComparison.CurrentCultureIgnoreCase)) + { + var nonce = getNounce(log.MsgIndex.GetValueOrDefault()); + if (nonce != null) + { + try + { + if (attr.Key.IsBase64String()) + { + attr.Key = Encoding.UTF8.GetString(await Encryption.Decrypt(Convert.FromBase64String(attr.Key), nonce)); + } + } + catch { } + try + { + if (attr.Value.IsBase64String()) + { + attr.Value = Encoding.UTF8.GetString(await Encryption.Decrypt(Convert.FromBase64String(attr.Value), nonce)); + } + } + catch { } + } + } + + arrayLog.Add(new CosmosArrayLog() + { + MsgIndex = log.MsgIndex.GetValueOrDefault(), + EventType = ev.MessageType, + Key = attr.Key, + Value = attr.Value, + }); + } + } + } + } + else if (tx.Code != 0 && !string.IsNullOrEmpty(tx.RawLog)) + { + try + { + var errorMatches = _errorMessageRegEx.Match(tx.RawLog); + if (errorMatches.Success && + !string.IsNullOrEmpty(errorMatches.Groups["msgIndex"]?.Value) && + !string.IsNullOrEmpty(errorMatches.Groups["encrypted"]?.Value)) + { + var encryptedError = Convert.FromBase64String(errorMatches.Groups["encrypted"].Value); + var msgIndex = Convert.ToInt32(errorMatches.Groups["msgIndex"].Value); + var nonce = getNounce(msgIndex); + if (nonce != null) + { + var decryptedBase64Error = Encoding.UTF8.GetString(await Encryption.Decrypt(encryptedError, nonce)); + + rawLog = rawLog.Replace($"encrypted: {errorMatches.Groups["encrypted"].Value}", decryptedBase64Error); + } + } + + // Check for errors + // Cosmos SDK Errors + // https://github.com/cosmos/cosmos-sdk/blob/main/types/errors/errors.go + if (tx.Codespace.Equals("sdk", StringComparison.CurrentCultureIgnoreCase)) + { + var isEnumParsed = System.Enum.TryParse(tx.Code.ToString(), true, out CosmosSdkErrorEnum errorEnum); + if (isEnumParsed) + { + secretTx.Exceptions.Add(new CosmosSdkException(errorEnum, txResponse)); + //throw new CosmosSdkException(errorEnum, encryptedResult); + } + } + } + catch (Exception ex) + { + // Not encrypted or can't decrypt because not original sender + secretTx.Exceptions.Add(new Exception("Not encrypted or can't decrypt because not original sender")); + secretTx.Exceptions.Add(ex); + } + } + + var txMsgData = TxMsgData.Parser.ParseFrom(Convert.FromHexString(tx.Data)); + var data = new byte[txMsgData.Data.Count][]; + for (int msgIndex = 0; msgIndex < txMsgData.Data.Count; msgIndex++) + { + data[msgIndex] = txMsgData.Data[msgIndex].Data.ToByteArray(); + if (data[msgIndex]?.Length > 0) + { + var nonce = getNounce(msgIndex); + if (nonce != null && tryToDecrypt) + { + var rawMsg = decodedTx?.Body?.Messages[msgIndex]; + var messageDecoder = MsgDecoderRegistry.Get(rawMsg.TypeUrl); + if (messageDecoder == null) continue; + + var msg = messageDecoder(rawMsg); + + if (rawMsg.TypeUrl.IsProtoType(MsgGrantAuthorization.MsgInstantiateContract)) + { + var decoded = Secret.Compute.V1Beta1.MsgInstantiateContractResponse.Parser.ParseFrom(txMsgData.Data[msgIndex].Data); + var decrypted = Convert.FromBase64String(Encoding.UTF8.GetString(await Encryption.Decrypt(decoded.Data.ToByteArray(), nonce))); + var decryptedMsg = new Secret.Compute.V1Beta1.MsgInstantiateContractResponse() + { + Address = decoded.Address, + Data = ByteString.CopyFrom(decrypted) + }; + data[msgIndex] = decryptedMsg.Encode(); + } + else if (rawMsg.TypeUrl.IsProtoType(MsgGrantAuthorization.MsgExecuteContract)) + { + var decoded = Secret.Compute.V1Beta1.MsgExecuteContractResponse.Parser.ParseFrom(txMsgData.Data[msgIndex].Data); + var decrypted = Convert.FromBase64String(Encoding.UTF8.GetString(await Encryption.Decrypt(decoded.Data.ToByteArray(), nonce))); + var jsonData = Encoding.UTF8.GetString(decrypted); + var decryptedMsg = new Secret.Compute.V1Beta1.MsgExecuteContractResponse() + { + Data = ByteString.CopyFrom(decrypted) + }; + data[msgIndex] = decryptedMsg.Encode(); + } + } + else + { + data[msgIndex] = txMsgData.Data[msgIndex].Data.ToByteArray(); + } + } + } + + secretTx.RawLog = rawLog; + secretTx.JsonLog = jsonLogs; + secretTx.ArrayLog = arrayLog; + secretTx.Data = data; + secretTx.Success = true; + + result.Add(secretTx); + + }); + + if (!result.IsEmpty) + { + return result.ToArray(); + } + + return null; + } + + } diff --git a/src/SecretNetworkClient.cs b/src/SecretNetworkClient.cs index 53071801..815317ee 100644 --- a/src/SecretNetworkClient.cs +++ b/src/SecretNetworkClient.cs @@ -113,7 +113,7 @@ public Queries Query { if (_queries == null) { - _queries = new Queries(this, Tx, _grpcChannel, _rpcMessageInterceptor); + _queries = new Queries(this, _grpcChannel, _rpcMessageInterceptor); } return _queries; } @@ -130,7 +130,7 @@ public TxClient Tx { if (_txClient == null) { - _txClient = new TxClient(this, _grpcChannel, _rpcMessageInterceptor); + _txClient = new TxClient(this, Query, _grpcChannel, _rpcMessageInterceptor); } return _txClient; } @@ -243,8 +243,13 @@ public async Task SignDirect(MsgBase[] messages, StdFee fee, SignerData s for (int i = 0; i < messages.Length; i++) { var msg = messages[i]; + if (msg is MsgExecuteContractBase) + { + ((MsgExecuteContractBase)msg).EncryptionUtils = EncryptionUtils; + } + await PopulateCodeHash(msg); - var protoMsg = await msg.ToProto(EncryptionUtils); + var protoMsg = await msg.ToProto(); encryptionNonces.TryAdd(i, ExtractNonce(protoMsg)); var any = Any.Pack(protoMsg,""); @@ -285,7 +290,11 @@ public async Task SignAmino(MsgBase[] messages, StdFee fee, SignerData si List msgs = new List(); foreach (var msg in messages) { - msgs.Add(await msg.ToAmino(EncryptionUtils)); + if (msg is MsgExecuteContractBase) + { + ((MsgExecuteContractBase)msg).EncryptionUtils = EncryptionUtils; + } + msgs.Add(await msg.ToAmino()); } var signDoc = MakeSignDocAmino(msgs.ToArray(), fee, ChainId, signerData.AccountNumber, signerData.Sequence, memo); @@ -297,8 +306,13 @@ public async Task SignAmino(MsgBase[] messages, StdFee fee, SignerData si for (int i = 0; i < messages.Length; i++) { var msg = messages[i]; + if (msg is MsgExecuteContractBase) + { + ((MsgExecuteContractBase)msg).EncryptionUtils = EncryptionUtils; + } + await PopulateCodeHash(msg); - var protoMsg = await msg.ToProto(EncryptionUtils); + var protoMsg = await msg.ToProto(); encryptionNonces.TryAdd(i, ExtractNonce(protoMsg)); var any = Any.Pack(protoMsg, ""); diff --git a/src/Tx/Authz.Msg.cs b/src/Tx/Authz.Msg.cs index dae542c8..f314df09 100644 --- a/src/Tx/Authz.Msg.cs +++ b/src/Tx/Authz.Msg.cs @@ -9,7 +9,7 @@ public class SendAuthorization : MsgBase public Coin[] Coins { get; set; } - public override async Task ToProto(SecretEncryptionUtils utils) + public override async Task ToProto() { var sendAuthorization = new Cosmos.Bank.V1Beta1.SendAuthorization(); sendAuthorization.SpendLimit.Add(Coins.Select(c => new Cosmos.Base.V1Beta1.Coin() { Amount = c.Amount, Denom = c.Denom }).ToArray()); @@ -17,7 +17,7 @@ public override async Task ToProto(SecretEncryptionUtils utils) return sendAuthorization; } - public override async Task ToAmino(SecretEncryptionUtils utils) + public override async Task ToAmino() { throw new Exception("SendAuthorization ToAmino is not implemented."); } @@ -68,7 +68,7 @@ public class StakeAuthorization : MsgBase /// public StakeAuthorizationType AuthorizationType { get; set; } - public override async Task ToProto(SecretEncryptionUtils utils) + public override async Task ToProto() { var sendAuthorization = new Cosmos.Staking.V1Beta1.StakeAuthorization(); if (MaxTokens != null) @@ -100,7 +100,7 @@ public override async Task ToProto(SecretEncryptionUtils utils) return sendAuthorization; } - public override async Task ToAmino(SecretEncryptionUtils utils) + public override async Task ToAmino() { throw new Exception("SendAuthorization ToAmino is not implemented."); } @@ -125,7 +125,7 @@ public class MsgGrant : MsgBase /// public ulong Expiration { get; set; } - public override async Task ToProto(SecretEncryptionUtils utils) + public override async Task ToProto() { Cosmos.Authz.V1Beta1.MsgGrant msgGrant = null; @@ -139,7 +139,7 @@ public override async Task ToProto(SecretEncryptionUtils utils) { Grant = new Cosmos.Authz.V1Beta1.Grant() { - Authorization = Any.Pack((await Authorization.ToProto(utils)),""), + Authorization = Any.Pack((await Authorization.ToProto()),""), Expiration = new Timestamp() { Seconds = (long)Expiration @@ -178,7 +178,7 @@ public override async Task ToProto(SecretEncryptionUtils utils) return null; } - public override async Task ToAmino(SecretEncryptionUtils utils) + public override async Task ToAmino() { throw new Exception("MsgGrant ToAmino is not implemented."); } @@ -201,7 +201,7 @@ public class MsgExec : MsgBase /// public MsgBase[] AuthorizationMsgs { get; set; } - public override async Task ToProto(SecretEncryptionUtils utils) + public override async Task ToProto() { if (AuthorizationMsgs != null && AuthorizationMsgs.Length > 0) { @@ -213,7 +213,7 @@ public override async Task ToProto(SecretEncryptionUtils utils) var protoMsgs = new List(); foreach (var m in AuthorizationMsgs) { - var protoMsg = await m.ToProto(utils); + var protoMsg = await m.ToProto(); protoMsgs.Add(Any.Pack(protoMsg, "")); } @@ -224,7 +224,7 @@ public override async Task ToProto(SecretEncryptionUtils utils) return null; } - public override Task ToAmino(SecretEncryptionUtils utils) + public override Task ToAmino() { throw new Exception("MsgExec ToAmino is not implemented."); } @@ -254,7 +254,7 @@ public class MsgRevoke : MsgBase /// public MsgBase[] AuthorizationMsgs { get; set; } - public override async Task ToProto(SecretEncryptionUtils utils) + public override async Task ToProto() { if (RevokeAuthorization != null) { @@ -274,7 +274,7 @@ public override async Task ToProto(SecretEncryptionUtils utils) return null; } - public override Task ToAmino(SecretEncryptionUtils utils) + public override Task ToAmino() { throw new NotImplementedException("MsgRevoke ToAmino is not implemented."); } diff --git a/src/Tx/AuthzTx .Simulate.cs b/src/Tx/AuthzTx .Simulate.cs deleted file mode 100644 index e7b1f3f2..00000000 --- a/src/Tx/AuthzTx .Simulate.cs +++ /dev/null @@ -1,49 +0,0 @@ -namespace SecretNET.Tx; - -public class AuthzTxSimulate -{ - private TxClient _tx; - - internal AuthzTxSimulate(TxClient tx) - { - _tx = tx; - } - - /// - /// MsgExec attempts to execute the provided messages using - /// authorizations granted to the grantee. Each message should have only - /// one signer corresponding to the granter of the authorization. - /// - /// - /// - /// - public async Task Exec(MsgExec msg, TxOptions? txOptions = null) - { - return await _tx.Simulate(msg, txOptions); - } - - /// - /// MsgGrant is a request type for Grant method. It declares authorization to the grantee - /// on behalf of the granter with the provided expiration time. - /// - /// - /// - /// - public async Task Grant(MsgGrant msg, TxOptions? txOptions = null) - { - return await _tx.Simulate(msg, txOptions); - } - - /// - /// MsgRevoke revokes any authorization with the provided sdk.Msg type on the - /// granter's account with that has been granted to the grantee. - /// - /// - /// - /// - public async Task Revoke(MsgRevoke msg, TxOptions? txOptions = null) - { - return await _tx.Simulate(msg, txOptions); - } - -} diff --git a/src/Tx/AuthzTx.cs b/src/Tx/AuthzTx.cs index 0fe7271a..feb8fb48 100644 --- a/src/Tx/AuthzTx.cs +++ b/src/Tx/AuthzTx.cs @@ -3,17 +3,10 @@ public class AuthzTx { private TxClient _tx; - private AuthzTxSimulate _txSimulte; internal AuthzTx(TxClient tx) { _tx = tx; - _txSimulte = new AuthzTxSimulate(tx); - } - - public AuthzTxSimulate Simulate - { - get { return _txSimulte; } } /// @@ -24,9 +17,10 @@ public AuthzTxSimulate Simulate /// /// /// - public async Task Exec(MsgExec msg, TxOptions? txOptions = null) + public async Task> Exec(Cosmos.Authz.V1Beta1.MsgExec msg, TxOptions? txOptions = null) { - return await _tx.Broadcast(msg, txOptions); + var txResult = await _tx.Broadcast(msg, txOptions); + return new SingleSecretTx(txResult); } /// @@ -36,9 +30,10 @@ public async Task Exec(MsgExec msg, TxOptions? txOptions = null) /// /// /// - public async Task Grant(MsgGrant msg, TxOptions? txOptions = null) + public async Task> Grant(Cosmos.Authz.V1Beta1.MsgGrant msg, TxOptions? txOptions = null) { - return await _tx.Broadcast(msg, txOptions); + var txResult = await _tx.Broadcast(msg, txOptions); + return new SingleSecretTx(txResult); } /// @@ -48,9 +43,10 @@ public async Task Grant(MsgGrant msg, TxOptions? txOptions = null) /// /// /// - public async Task Revoke(MsgRevoke msg, TxOptions? txOptions = null) + public async Task> Revoke(Cosmos.Authz.V1Beta1.MsgRevoke msg, TxOptions? txOptions = null) { - return await _tx.Broadcast(msg, txOptions); + var txResult = await _tx.Broadcast(msg, txOptions); + return new SingleSecretTx(txResult); } } diff --git a/src/Tx/Bank.Msg.cs b/src/Tx/Bank.Msg.cs index 198b029e..1caf84a6 100644 --- a/src/Tx/Bank.Msg.cs +++ b/src/Tx/Bank.Msg.cs @@ -23,7 +23,7 @@ public MsgSend(string fromAddress, string toAddress, Coin[] amount) Amount = amount; } - public override async Task ToProto(SecretEncryptionUtils utils) + public override async Task ToProto() { var msgSend = new Cosmos.Bank.V1Beta1.MsgSend() { @@ -35,7 +35,7 @@ public override async Task ToProto(SecretEncryptionUtils utils) return msgSend; } - public override async Task ToAmino(SecretEncryptionUtils utils) + public override async Task ToAmino() { var aminoMsg = new AminoMsg("cosmos-sdk/MsgSend"); // order of properties must be sorted for amino signing!! @@ -107,7 +107,7 @@ public MsgMultiSend(Input[] inputs, Output[] outputs) Outputs = outputs; } - public override async Task ToProto(SecretEncryptionUtils utils) + public override async Task ToProto() { if (Inputs != null || Outputs != null) { @@ -146,7 +146,7 @@ public override async Task ToProto(SecretEncryptionUtils utils) return null; } - public override async Task ToAmino(SecretEncryptionUtils utils) + public override async Task ToAmino() { var aminoMsg = new AminoMsg("cosmos-sdk/MsgMultiSend"); // order of properties must be sorted for amino signing!! diff --git a/src/Tx/BankTx.Simulate.cs b/src/Tx/BankTx.Simulate.cs deleted file mode 100644 index 8ea2df89..00000000 --- a/src/Tx/BankTx.Simulate.cs +++ /dev/null @@ -1,34 +0,0 @@ -namespace SecretNET.Tx; - -public class BankTxSimulate -{ - private TxClient _tx; - - internal BankTxSimulate(TxClient tx) - { - _tx = tx; - } - - /// - /// Simulates "MsgSend represents a message to send coins from one account to another". - /// - /// - /// - /// - public async Task Send(MsgSend msg, TxOptions? txOptions = null) - { - return await _tx.Simulate(msg, txOptions); - } - - /// - /// Simulates "MsgMultiSend represents an arbitrary multi-in, multi-out send message". - /// - /// - /// - /// - public async Task MultiSend(MsgMultiSend msg, TxOptions? txOptions = null) - { - return await _tx.Simulate(msg, txOptions); - } - -} diff --git a/src/Tx/BankTx.cs b/src/Tx/BankTx.cs index 15e3cf5f..0faf7900 100644 --- a/src/Tx/BankTx.cs +++ b/src/Tx/BankTx.cs @@ -1,19 +1,14 @@ -namespace SecretNET.Tx; +using System.Net; + +namespace SecretNET.Tx; public class BankTx { private TxClient _tx; - private BankTxSimulate _txSimulte; internal BankTx(TxClient tx) { _tx = tx; - _txSimulte = new BankTxSimulate(tx); - } - - public BankTxSimulate Simulate - { - get { return _txSimulte; } } /// @@ -22,9 +17,30 @@ public BankTxSimulate Simulate /// /// /// - public async Task Send(MsgSend msg, TxOptions? txOptions = null) + public async Task> Send(Cosmos.Bank.V1Beta1.MsgSend msg, TxOptions? txOptions = null) + { + var txResult = await _tx.Broadcast(msg, txOptions); + return new SingleSecretTx(txResult); + } + + /// + /// Sends the specified to address. + /// + /// To address. + /// The amount. + /// The tx options. + /// SingleSecretTx<Cosmos.Bank.V1Beta1.MsgSendResponse>. + public async Task> Send(string toAddress, Coin[] amount, TxOptions? txOptions = null) { - return await _tx.Broadcast(msg, txOptions); + var msgSend = new Cosmos.Bank.V1Beta1.MsgSend() + { + FromAddress = _tx.WalletAddress, + ToAddress = toAddress, + }; + msgSend.Amount.Add(amount.Select(c => new Cosmos.Base.V1Beta1.Coin() { Amount = c.Amount, Denom = c.Denom }).ToArray()); + + var txResult = await _tx.Broadcast(msgSend, txOptions); + return new SingleSecretTx(txResult); } /// @@ -33,9 +49,10 @@ public async Task Send(MsgSend msg, TxOptions? txOptions = null) /// /// /// - public async Task MultiSend(MsgMultiSend msg, TxOptions? txOptions = null) + public async Task> MultiSend(Cosmos.Bank.V1Beta1.MsgMultiSend msg, TxOptions? txOptions = null) { - return await _tx.Broadcast(msg, txOptions); + var txResult = await _tx.Broadcast(msg, txOptions); + return new SingleSecretTx(txResult); } } diff --git a/src/Tx/Compute.Msg.cs b/src/Tx/Compute.Msg.cs index 65e59ac9..ffbfbaa7 100644 --- a/src/Tx/Compute.Msg.cs +++ b/src/Tx/Compute.Msg.cs @@ -9,6 +9,8 @@ public abstract class MsgExecuteContractBase : MsgBase public string Sender { get; set; } + internal SecretEncryptionUtils EncryptionUtils { get; set; } + /// /// The SHA256 hash value of the contract's WASM bytecode, represented as case-insensitive 64 /// character hex string. @@ -30,15 +32,15 @@ public abstract class MsgExecuteContractBase : MsgBase /// public object Msg { get; set; } - internal async Task GetEncryptedMsg(SecretEncryptionUtils utils) + internal async Task GetEncryptedMsg() { - if (_msgEncrypted == null) + if (_msgEncrypted == null && EncryptionUtils != null) { // The encryption uses a random nonce // toProto() & toAmino() are called multiple times during signing // so to keep the msg consistant across calls we encrypt the msg only once string msgString = (Msg is string) ? (string)Msg : JsonConvert.SerializeObject(Msg); - _msgEncrypted = await utils.Encrypt(CodeHash, msgString); + _msgEncrypted = await EncryptionUtils.Encrypt(CodeHash, msgString); } return _msgEncrypted; } @@ -98,11 +100,11 @@ public MsgExecuteContract(string contractAddress, object msg, string codeHash = WarnIfCodeHashMissing(); } - public override async Task ToProto(SecretEncryptionUtils utils) + public override async Task ToProto() { WarnIfCodeHashMissing(); - var msgEncrypted = await GetEncryptedMsg(utils); + var msgEncrypted = await GetEncryptedMsg(); var msgExecuteContract = new Secret.Compute.V1Beta1.MsgExecuteContract() { @@ -119,11 +121,11 @@ public override async Task ToProto(SecretEncryptionUtils utils) return msgExecuteContract; } - public override async Task ToAmino(SecretEncryptionUtils utils) + public override async Task ToAmino() { WarnIfCodeHashMissing(); - var msgEncrypted = await GetEncryptedMsg(utils); + var msgEncrypted = await GetEncryptedMsg(); var aminoMsg = new AminoMsg("wasm/MsgExecuteContract"); // order of properties must be sorted for amino signing!! @@ -166,11 +168,11 @@ public MsgInstantiateContract(ulong codeId, string label, object initMsg, string WarnIfCodeHashMissing(); } - public override async Task ToProto(SecretEncryptionUtils utils) + public override async Task ToProto() { WarnIfCodeHashMissing(); - var msgEncrypted = await GetEncryptedMsg(utils); + var msgEncrypted = await GetEncryptedMsg(); var msgInstantiateContract = new Secret.Compute.V1Beta1.MsgInstantiateContract() { @@ -188,11 +190,11 @@ public override async Task ToProto(SecretEncryptionUtils utils) return msgInstantiateContract; } - public override async Task ToAmino(SecretEncryptionUtils utils) + public override async Task ToAmino() { WarnIfCodeHashMissing(); - var msgEncrypted = await GetEncryptedMsg(utils); + var msgEncrypted = await GetEncryptedMsg(); var aminoMsg = new AminoMsg("wasm/MsgInstantiateContract"); // order of properties must be sorted for amino signing!! @@ -249,7 +251,7 @@ public MsgStoreCode(byte[] wasmByteCode, string source = null, string builder = Builder = builder; } - public override async Task ToProto(SecretEncryptionUtils utils) + public override async Task ToProto() { var msgStoreCode = new Secret.Compute.V1Beta1.MsgStoreCode() { @@ -262,7 +264,7 @@ public override async Task ToProto(SecretEncryptionUtils utils) return msgStoreCode; } - public override async Task ToAmino(SecretEncryptionUtils utils) + public override async Task ToAmino() { var aminoMsg = new AminoMsg("wasm/MsgStoreCode"); // order of properties must be sorted for amino signing!! @@ -277,35 +279,3 @@ public override async Task ToAmino(SecretEncryptionUtils utils) return aminoMsg; } } - - -// Typed SecretTx responses - -public class InstantiateContractSecretTx : SecretTx -{ - public string ContractAddress { get; set; } - - public InstantiateContractSecretTx(SecretTx secretTx) : base(secretTx) - { - ContractAddress = TryFindEventValue("contract_address"); - } -} - -public class StoreCodeSecretTx : SecretTx -{ - public ulong CodeId { get; set; } - - public StoreCodeSecretTx(SecretTx secretTx) : base(secretTx) - { - TryGetCodeId(); - } - - private void TryGetCodeId() - { - var codeIdString = TryFindEventValue("code_id"); - if (!string.IsNullOrEmpty(codeIdString) && ulong.TryParse(codeIdString, out var code_id)) - { - CodeId = code_id; - } - } -} \ No newline at end of file diff --git a/src/Tx/ComputeTx.Simulate.cs b/src/Tx/ComputeTx.Simulate.cs deleted file mode 100644 index 7902bef4..00000000 --- a/src/Tx/ComputeTx.Simulate.cs +++ /dev/null @@ -1,78 +0,0 @@ -namespace SecretNET.Tx; - -public class ComputeTxSimulate -{ - private TxClient _tx; - - internal ComputeTxSimulate(TxClient tx) - { - _tx = tx; - } - - /// - /// Simulates "Execute a function on a contract". - /// - /// - /// - /// - public async Task ExecuteContract(MsgExecuteContract msg, TxOptions txOptions = null) - { - if (string.IsNullOrEmpty(msg.Sender)) - { - msg.Sender = _tx.secretClient.WalletAddress; - } - return await _tx.Simulate(msg, txOptions); - } - - /// - /// Simulate: Execute a function on a contract - /// - /// - /// - /// - /// - public async Task ExecuteContract(MsgExecuteContract msg, TxOptions txOptions = null) - { - return await ExecuteContract(msg, txOptions); - } - - /// - /// Simulates "Upload a compiled contract to Secret Network". - /// - /// - /// - /// - public async Task StoreCode(MsgStoreCode msg, TxOptions txOptions = null) - { - if (msg == null || msg.WasmByteCode == null || msg.WasmByteCode.Length == 0) return null; - - if (!await msg.WasmByteCode.IsGzip()) - { - msg.WasmByteCode = await msg.WasmByteCode.CompressGzip(); - } - - if (string.IsNullOrEmpty(msg.Sender)) - { - msg.Sender = _tx.secretClient.WalletAddress; - } - - return await _tx.Simulate(msg, txOptions); - } - - /// - /// Simulates "Instantiate a contract from code id". - /// - /// - /// - /// - public async Task InstantiateContract(MsgInstantiateContract msg, TxOptions txOptions = null) - { - if (string.IsNullOrEmpty(msg.Sender)) - { - msg.Sender = _tx.secretClient.WalletAddress; - } - - return await _tx.Simulate(msg, txOptions); - } - -} diff --git a/src/Tx/ComputeTx.cs b/src/Tx/ComputeTx.cs index 20169401..b4f18657 100644 --- a/src/Tx/ComputeTx.cs +++ b/src/Tx/ComputeTx.cs @@ -3,20 +3,12 @@ public class ComputeTx { private TxClient _tx; - private ComputeTxSimulate _txSimulte; internal ComputeTx(TxClient tx) { _tx = tx; - _txSimulte = new ComputeTxSimulate(tx); } - public ComputeTxSimulate Simulate - { - get { return _txSimulte; } - } - - /// /// Execute a function on a contract /// @@ -55,7 +47,7 @@ public async Task> ExecuteContract(MsgExecuteContract msg, /// /// /// - public async Task StoreCode(MsgStoreCode msg, TxOptions txOptions = null) + public async Task> StoreCode(MsgStoreCode msg, TxOptions txOptions = null) { if (msg == null || msg.WasmByteCode == null || msg.WasmByteCode.Length == 0) return null; @@ -69,8 +61,8 @@ public async Task StoreCode(MsgStoreCode msg, TxOptions txOpt msg.Sender = _tx.secretClient.WalletAddress; } - var secretTx = await _tx.Broadcast(msg, txOptions); - return new StoreCodeSecretTx(secretTx); + var txResult = await _tx.Broadcast(msg, txOptions); + return new SingleSecretTx(txResult); } /// @@ -79,15 +71,15 @@ public async Task StoreCode(MsgStoreCode msg, TxOptions txOpt /// /// /// - public async Task InstantiateContract(MsgInstantiateContract msg, TxOptions txOptions = null) + public async Task> InstantiateContract(MsgInstantiateContract msg, TxOptions txOptions = null) { if (string.IsNullOrEmpty(msg.Sender)) { msg.Sender = _tx.secretClient.WalletAddress; } - var secretTx = await _tx.Broadcast(msg, txOptions); - return new InstantiateContractSecretTx(secretTx); + var txResult = await _tx.Broadcast(msg, txOptions); + return new SingleSecretTx(txResult); } } diff --git a/src/Tx/Crisis.Msg.cs b/src/Tx/Crisis.Msg.cs index 566ac170..ffb60d9d 100644 --- a/src/Tx/Crisis.Msg.cs +++ b/src/Tx/Crisis.Msg.cs @@ -21,7 +21,7 @@ public MsgVerifyInvariant(string sender, string invariantModuleName, string inva } - public override async Task ToProto(SecretEncryptionUtils utils) + public override async Task ToProto() { var msgVerifyInvariant = new Cosmos.Crisis.V1Beta1.MsgVerifyInvariant() { @@ -33,7 +33,7 @@ public override async Task ToProto(SecretEncryptionUtils utils) return msgVerifyInvariant; } - public override async Task ToAmino(SecretEncryptionUtils utils) + public override async Task ToAmino() { var aminoMsg = new AminoMsg("cosmos-sdk/MsgVerifyInvariant"); // order of properties must be sorted for amino signing!! diff --git a/src/Tx/CrisisTx.Simulate.cs b/src/Tx/CrisisTx.Simulate.cs deleted file mode 100644 index 949ef269..00000000 --- a/src/Tx/CrisisTx.Simulate.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace SecretNET.Tx; - -public class CrisisTxSimulate -{ - private TxClient _tx; - - internal CrisisTxSimulate(TxClient tx) - { - _tx = tx; - } - - /// - /// Simulates "MsgVerifyInvariant represents a message to verify a particular invariance". - /// - /// - /// - /// - public async Task VerifyInvariant(MsgVerifyInvariant msg, TxOptions? txOptions = null) - { - return await _tx.Simulate(msg, txOptions); - } - -} - \ No newline at end of file diff --git a/src/Tx/CrisisTx.cs b/src/Tx/CrisisTx.cs index c6722df1..5e9bf290 100644 --- a/src/Tx/CrisisTx.cs +++ b/src/Tx/CrisisTx.cs @@ -3,17 +3,10 @@ public class CrisisTx { private TxClient _tx; - private CrisisTxSimulate _txSimulte; internal CrisisTx(TxClient tx) { _tx = tx; - _txSimulte = new CrisisTxSimulate(tx); - } - - public CrisisTxSimulate Simulate - { - get { return _txSimulte; } } /// @@ -22,9 +15,10 @@ public CrisisTxSimulate Simulate /// /// /// - public async Task VerifyInvariant(MsgVerifyInvariant msg, TxOptions? txOptions = null) + public async Task> VerifyInvariant(Cosmos.Crisis.V1Beta1.MsgVerifyInvariant msg, TxOptions? txOptions = null) { - return await _tx.Broadcast(msg, txOptions); + var txResult = await _tx.Broadcast(msg, txOptions); + return new SingleSecretTx(txResult); } } diff --git a/src/Tx/Distribution.Msg.cs b/src/Tx/Distribution.Msg.cs index cf8b7807..7ce367e8 100644 --- a/src/Tx/Distribution.Msg.cs +++ b/src/Tx/Distribution.Msg.cs @@ -21,7 +21,7 @@ public MsgSetWithdrawAddress(string delegatorAddress, string withdrawAddress) } - public override async Task ToProto(SecretEncryptionUtils utils) + public override async Task ToProto() { var msgSetWithdrawAddress = new Cosmos.Distribution.V1Beta1.MsgSetWithdrawAddress() { @@ -32,7 +32,7 @@ public override async Task ToProto(SecretEncryptionUtils utils) return msgSetWithdrawAddress; } - public override async Task ToAmino(SecretEncryptionUtils utils) + public override async Task ToAmino() { var aminoMsg = new AminoMsg("cosmos-sdk/MsgModifyWithdrawAddress"); // order of properties must be sorted for amino signing!! @@ -67,7 +67,7 @@ public MsgWithdrawDelegatorReward(string delegatorAddress, string validatorAddre } - public override async Task ToProto(SecretEncryptionUtils utils) + public override async Task ToProto() { var msgWithdrawDelegatorReward = new Cosmos.Distribution.V1Beta1.MsgWithdrawDelegatorReward() { @@ -78,7 +78,7 @@ public override async Task ToProto(SecretEncryptionUtils utils) return msgWithdrawDelegatorReward; } - public override async Task ToAmino(SecretEncryptionUtils utils) + public override async Task ToAmino() { var aminoMsg = new AminoMsg("cosmos-sdk/MsgWithdrawDelegationReward"); // order of properties must be sorted for amino signing!! @@ -109,7 +109,7 @@ public MsgWithdrawValidatorCommission(string validatorAddress) ValidatorAddress = validatorAddress; } - public override async Task ToProto(SecretEncryptionUtils utils) + public override async Task ToProto() { var msgWithdrawValidatorCommission = new Cosmos.Distribution.V1Beta1.MsgWithdrawValidatorCommission() { @@ -119,7 +119,7 @@ public override async Task ToProto(SecretEncryptionUtils utils) return msgWithdrawValidatorCommission; } - public override async Task ToAmino(SecretEncryptionUtils utils) + public override async Task ToAmino() { var aminoMsg = new AminoMsg("cosmos-sdk/MsgWithdrawValidatorCommission"); // order of properties must be sorted for amino signing!! @@ -152,7 +152,7 @@ public MsgFundCommunityPool(string depositor, Coin[] amount) Amount = amount; } - public override async Task ToProto(SecretEncryptionUtils utils) + public override async Task ToProto() { var msgFundCommunityPool = new Cosmos.Distribution.V1Beta1.MsgFundCommunityPool() { @@ -167,7 +167,7 @@ public override async Task ToProto(SecretEncryptionUtils utils) return msgFundCommunityPool; } - public override async Task ToAmino(SecretEncryptionUtils utils) + public override async Task ToAmino() { var aminoMsg = new AminoMsg("cosmos-sdk/MsgFundCommunityPool"); // order of properties must be sorted for amino signing!! diff --git a/src/Tx/DistributionTx.Simulate.cs b/src/Tx/DistributionTx.Simulate.cs deleted file mode 100644 index 33bc6b5b..00000000 --- a/src/Tx/DistributionTx.Simulate.cs +++ /dev/null @@ -1,57 +0,0 @@ -namespace SecretNET.Tx; - -public class DistributionTxSimulate -{ - private TxClient _tx; - - internal DistributionTxSimulate(TxClient tx) - { - _tx = tx; - } - - /// - /// Simulates "MsgFundCommunityPool allows an account to directly fund the community pool". - /// - /// - /// - /// - public async Task FundCommunityPool(MsgFundCommunityPool msg, TxOptions? txOptions = null) - { - return await _tx.Simulate(msg, txOptions); - } - - /// - /// Simulates "MsgSetWithdrawAddress sets the withdraw address for a delegator (or validator self-delegation)". - /// - /// - /// - /// - public async Task SetWithdrawAddress(MsgSetWithdrawAddress msg, TxOptions? txOptions = null) - { - return await _tx.Simulate(msg, txOptions); - } - - /// - /// Simulates "MsgWithdrawDelegatorReward represents delegation withdrawal to a delegator from a single validator". - /// - /// - /// - /// - public async Task WithdrawDelegatorReward(MsgWithdrawDelegatorReward msg, TxOptions? txOptions = null) - { - return await _tx.Simulate(msg, txOptions); - } - - /// - /// Simulates "MsgWithdrawValidatorCommission withdraws the full commission to the validator address". - /// - /// - /// - /// - public async Task WithdrawValidatorCommission(MsgWithdrawValidatorCommission msg, TxOptions? txOptions = null) - { - return await _tx.Simulate(msg, txOptions); - } - -} - \ No newline at end of file diff --git a/src/Tx/DistributionTx.cs b/src/Tx/DistributionTx.cs index 2ab3d677..1ef0e144 100644 --- a/src/Tx/DistributionTx.cs +++ b/src/Tx/DistributionTx.cs @@ -3,17 +3,9 @@ public class DistributionTx { private TxClient _tx; - private DistributionTxSimulate _txSimulte; - internal DistributionTx(TxClient tx) { _tx = tx; - _txSimulte = new DistributionTxSimulate(tx); - } - - public DistributionTxSimulate Simulate - { - get { return _txSimulte; } } /// @@ -22,9 +14,10 @@ public DistributionTxSimulate Simulate /// /// /// - public async Task FundCommunityPool(MsgFundCommunityPool msg, TxOptions? txOptions = null) + public async Task> FundCommunityPool(Cosmos.Distribution.V1Beta1.MsgFundCommunityPool msg, TxOptions? txOptions = null) { - return await _tx.Broadcast(msg, txOptions); + var txResult = await _tx.Broadcast(msg, txOptions); + return new SingleSecretTx(txResult); } /// @@ -33,9 +26,10 @@ public async Task FundCommunityPool(MsgFundCommunityPool msg, TxOption /// /// /// - public async Task SetWithdrawAddress(MsgSetWithdrawAddress msg, TxOptions? txOptions = null) + public async Task> SetWithdrawAddress(Cosmos.Distribution.V1Beta1.MsgSetWithdrawAddress msg, TxOptions? txOptions = null) { - return await _tx.Broadcast(msg, txOptions); + var txResult = await _tx.Broadcast(msg, txOptions); + return new SingleSecretTx(txResult); } /// @@ -44,9 +38,10 @@ public async Task SetWithdrawAddress(MsgSetWithdrawAddress msg, TxOpti /// /// /// - public async Task WithdrawDelegatorReward(MsgWithdrawDelegatorReward msg, TxOptions? txOptions = null) + public async Task> WithdrawDelegatorReward(Cosmos.Distribution.V1Beta1.MsgWithdrawDelegatorReward msg, TxOptions? txOptions = null) { - return await _tx.Broadcast(msg, txOptions); + var txResult = await _tx.Broadcast(msg, txOptions); + return new SingleSecretTx(txResult); } /// @@ -55,9 +50,10 @@ public async Task WithdrawDelegatorReward(MsgWithdrawDelegatorReward m /// /// /// - public async Task WithdrawValidatorCommission(MsgWithdrawValidatorCommission msg, TxOptions? txOptions = null) + public async Task> WithdrawValidatorCommission(Cosmos.Distribution.V1Beta1.MsgWithdrawValidatorCommission msg, TxOptions? txOptions = null) { - return await _tx.Broadcast(msg, txOptions); + var txResult = await _tx.Broadcast(msg, txOptions); + return new SingleSecretTx(txResult); } } diff --git a/src/Tx/Evidence.Msg.cs b/src/Tx/Evidence.Msg.cs index 1743000b..39fa5a41 100644 --- a/src/Tx/Evidence.Msg.cs +++ b/src/Tx/Evidence.Msg.cs @@ -19,12 +19,12 @@ public MsgSubmitEvidence(string submitter, Any evidence) Evidence = evidence; } - public override async Task ToProto(SecretEncryptionUtils utils) + public override async Task ToProto() { throw new NotImplementedException("MsgSubmitEvidence ToProto is not implemented."); } - public override async Task ToAmino(SecretEncryptionUtils utils) + public override async Task ToAmino() { throw new NotImplementedException("MsgSubmitEvidence ToAmino is not implemented."); } diff --git a/src/Tx/EvidenceTx.Simulate.cs b/src/Tx/EvidenceTx.Simulate.cs deleted file mode 100644 index 3595ae04..00000000 --- a/src/Tx/EvidenceTx.Simulate.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace SecretNET.Tx; - -public class EvidenceTxSimulate -{ - private TxClient _tx; - - internal EvidenceTxSimulate(TxClient tx) - { - _tx = tx; - } - - /// - /// Simulates "MsgSubmitEvidence represents a message that supports submitting arbitrary - /// Evidence of misbehavior such as equivocation or counterfactual signing." - /// - /// - /// - /// - public async Task SubmitEvidence(MsgSubmitEvidence msg, TxOptions? txOptions = null) - { - return await _tx.Simulate(msg, txOptions); - } - -} - \ No newline at end of file diff --git a/src/Tx/EvidenceTx.cs b/src/Tx/EvidenceTx.cs index c22b9024..3823a28e 100644 --- a/src/Tx/EvidenceTx.cs +++ b/src/Tx/EvidenceTx.cs @@ -3,17 +3,10 @@ public class EvidenceTx { private TxClient _tx; - private EvidenceTxSimulate _txSimulte; internal EvidenceTx(TxClient tx) { _tx = tx; - _txSimulte = new EvidenceTxSimulate(tx); - } - - public EvidenceTxSimulate Simulate - { - get { return _txSimulte; } } /// @@ -23,9 +16,10 @@ public EvidenceTxSimulate Simulate /// /// /// - public async Task SubmitEvidence(MsgSubmitEvidence msg, TxOptions? txOptions = null) + public async Task> SubmitEvidence(Cosmos.Evidence.V1Beta1.MsgSubmitEvidence msg, TxOptions? txOptions = null) { - return await _tx.Broadcast(msg, txOptions); + var txResult = await _tx.Broadcast(msg, txOptions); + return new SingleSecretTx(txResult); } } diff --git a/src/Tx/Feegrant.Msg.cs b/src/Tx/Feegrant.Msg.cs index 8a895cc7..084ed8c3 100644 --- a/src/Tx/Feegrant.Msg.cs +++ b/src/Tx/Feegrant.Msg.cs @@ -32,7 +32,7 @@ public MsgGrantAllowance(string granter, string grantee, Any allowance) } - public override async Task ToProto(SecretEncryptionUtils utils) + public override async Task ToProto() { var msgGrantAllowance = new Cosmos.Feegrant.V1Beta1.MsgGrantAllowance() { @@ -44,7 +44,7 @@ public override async Task ToProto(SecretEncryptionUtils utils) return msgGrantAllowance; } - public override async Task ToAmino(SecretEncryptionUtils utils) + public override async Task ToAmino() { var aminoMsg = new AminoMsg("cosmos-sdk/MsgGrantAllowance"); // order of properties must be sorted for amino signing!! @@ -84,7 +84,7 @@ public MsgRevokeAllowance(string granter, string grantee, Any allowance) } - public override async Task ToProto(SecretEncryptionUtils utils) + public override async Task ToProto() { var msgRevokeAllowance = new Cosmos.Feegrant.V1Beta1.MsgRevokeAllowance() { @@ -95,7 +95,7 @@ public override async Task ToProto(SecretEncryptionUtils utils) return msgRevokeAllowance; } - public override async Task ToAmino(SecretEncryptionUtils utils) + public override async Task ToAmino() { var aminoMsg = new AminoMsg("cosmos-sdk/MsgRevokeAllowance"); // order of properties must be sorted for amino signing!! diff --git a/src/Tx/FeegrantTx.Simulate.cs b/src/Tx/FeegrantTx.Simulate.cs deleted file mode 100644 index 3e590ea4..00000000 --- a/src/Tx/FeegrantTx.Simulate.cs +++ /dev/null @@ -1,35 +0,0 @@ -namespace SecretNET.Tx; - -public class FeegrantTxSimulate -{ - private TxClient _tx; - - internal FeegrantTxSimulate(TxClient tx) - { - _tx = tx; - } - - /// - /// Simulates "MsgGrantAllowance adds permission for Grantee to spend up to Allowance of fees from the account of Granter". - /// - /// - /// - /// - public async Task GrantAllowance(MsgGrantAllowance msg, TxOptions? txOptions = null) - { - return await _tx.Simulate(msg, txOptions); - } - - /// - /// Simulates "MsgRevokeAllowance removes any existing Allowance from Granter to Grantee". - /// - /// - /// - /// - public async Task RevokeAllowance(MsgRevokeAllowance msg, TxOptions? txOptions = null) - { - return await _tx.Simulate(msg, txOptions); - } - -} - \ No newline at end of file diff --git a/src/Tx/FeegrantTx.cs b/src/Tx/FeegrantTx.cs index b5b1bc04..5da79891 100644 --- a/src/Tx/FeegrantTx.cs +++ b/src/Tx/FeegrantTx.cs @@ -3,17 +3,10 @@ public class FeegrantTx { private TxClient _tx; - private FeegrantTxSimulate _txSimulte; internal FeegrantTx(TxClient tx) { _tx = tx; - _txSimulte = new FeegrantTxSimulate(tx); - } - - public FeegrantTxSimulate Simulate - { - get { return _txSimulte; } } /// @@ -22,9 +15,10 @@ public FeegrantTxSimulate Simulate /// /// /// - public async Task GrantAllowance(MsgGrantAllowance msg, TxOptions? txOptions = null) + public async Task> GrantAllowance(Cosmos.Feegrant.V1Beta1.MsgGrantAllowance msg, TxOptions? txOptions = null) { - return await _tx.Broadcast(msg, txOptions); + var txResult = await _tx.Broadcast(msg, txOptions); + return new SingleSecretTx(txResult); } /// @@ -33,9 +27,10 @@ public async Task GrantAllowance(MsgGrantAllowance msg, TxOptions? txO /// /// /// - public async Task RevokeAllowance(MsgRevokeAllowance msg, TxOptions? txOptions = null) + public async Task> RevokeAllowance(Cosmos.Feegrant.V1Beta1.MsgRevokeAllowance msg, TxOptions? txOptions = null) { - return await _tx.Broadcast(msg, txOptions); + var txResult = await _tx.Broadcast(msg, txOptions); + return new SingleSecretTx(txResult); } } diff --git a/src/Tx/Gov.Msg.cs b/src/Tx/Gov.Msg.cs index da42b158..3ebd510e 100644 --- a/src/Tx/Gov.Msg.cs +++ b/src/Tx/Gov.Msg.cs @@ -99,7 +99,7 @@ public MsgSubmitProposal(ProposalType type, Coin[] initialDeposit, string propos } - public override async Task ToProto(SecretEncryptionUtils utils) + public override async Task ToProto() { if (Type == ProposalType.TextProposal) { @@ -148,7 +148,7 @@ public override async Task ToProto(SecretEncryptionUtils utils) return null; } - public override async Task ToAmino(SecretEncryptionUtils utils) + public override async Task ToAmino() { throw new NotImplementedException("MsgSubmitProposal ToAmino is not implemented."); } @@ -177,7 +177,7 @@ public MsgVote(Cosmos.Gov.V1Beta1.VoteOption option, string voter, ulong proposa ProposalId = proposalId; } - public override async Task ToProto(SecretEncryptionUtils utils) + public override async Task ToProto() { var msgVote = new Cosmos.Gov.V1Beta1.MsgVote() { @@ -188,7 +188,7 @@ public override async Task ToProto(SecretEncryptionUtils utils) return msgVote; } - public override async Task ToAmino(SecretEncryptionUtils utils) + public override async Task ToAmino() { var aminoMsg = new AminoMsg("cosmos-sdk/MsgVote"); // order of properties must be sorted for amino signing!! @@ -224,7 +224,7 @@ public MsgVoteWeighted(Cosmos.Gov.V1Beta1.WeightedVoteOption[] options, string v ProposalId = proposalId; } - public override async Task ToProto(SecretEncryptionUtils utils) + public override async Task ToProto() { var msgVoteWeighted = new Cosmos.Gov.V1Beta1.MsgVoteWeighted() { @@ -239,7 +239,7 @@ public override async Task ToProto(SecretEncryptionUtils utils) return msgVoteWeighted; } - public override async Task ToAmino(SecretEncryptionUtils utils) + public override async Task ToAmino() { var aminoMsg = new AminoMsg("cosmos-sdk/MsgVoteWeighted"); // order of properties must be sorted for amino signing!! @@ -280,7 +280,7 @@ public MsgDeposit(string depositor, ulong proposalId, Coin[] amount) Amount = amount; } - public override async Task ToProto(SecretEncryptionUtils utils) + public override async Task ToProto() { var msgDeposit = new Cosmos.Gov.V1Beta1.MsgDeposit() { @@ -295,7 +295,7 @@ public override async Task ToProto(SecretEncryptionUtils utils) return msgDeposit; } - public override async Task ToAmino(SecretEncryptionUtils utils) + public override async Task ToAmino() { var aminoMsg = new AminoMsg("cosmos-sdk/MsgVoteWeighted"); // order of properties must be sorted for amino signing!! diff --git a/src/Tx/GovTx.Simulate.cs b/src/Tx/GovTx.Simulate.cs deleted file mode 100644 index 737691dc..00000000 --- a/src/Tx/GovTx.Simulate.cs +++ /dev/null @@ -1,57 +0,0 @@ -namespace SecretNET.Tx; - -public class GovTxSimulate -{ - private TxClient _tx; - - internal GovTxSimulate(TxClient tx) - { - _tx = tx; - } - - /// - /// Simulates "MsgDeposit defines a message to submit a deposit to an existing proposal". - /// - /// - /// - /// - public async Task Deposit(MsgDeposit msg, TxOptions? txOptions = null) - { - return await _tx.Simulate(msg, txOptions); - } - - /// - /// Simulates "MsgSubmitProposal defines an sdk.Msg type that supports submitting arbitrary proposal Content". - /// - /// - /// - /// - public async Task SubmitProposal(MsgSubmitProposal msg, TxOptions? txOptions = null) - { - return await _tx.Simulate(msg, txOptions); - } - - /// - /// Simulates "MsgVote defines a message to cast a vote. - /// - /// - /// - /// - public async Task Vote(MsgVote msg, TxOptions? txOptions = null) - { - return await _tx.Simulate(msg, txOptions); - } - - /// - /// Simulates "MsgVoteWeighted defines a message to cast a vote, with an option to split the vote". - /// - /// - /// - /// - public async Task VoteWeighted(MsgVoteWeighted msg, TxOptions? txOptions = null) - { - return await _tx.Simulate(msg, txOptions); - } - -} - \ No newline at end of file diff --git a/src/Tx/GovTx.cs b/src/Tx/GovTx.cs index c05a8239..24b0639e 100644 --- a/src/Tx/GovTx.cs +++ b/src/Tx/GovTx.cs @@ -3,17 +3,10 @@ public class GovTx { private TxClient _tx; - private GovTxSimulate _txSimulte; internal GovTx(TxClient tx) { _tx = tx; - _txSimulte = new GovTxSimulate(tx); - } - - public GovTxSimulate Simulate - { - get { return _txSimulte; } } /// @@ -22,9 +15,10 @@ public GovTxSimulate Simulate /// /// /// - public async Task Deposit(MsgDeposit msg, TxOptions? txOptions = null) + public async Task> Deposit(Cosmos.Gov.V1Beta1.MsgDeposit msg, TxOptions? txOptions = null) { - return await _tx.Broadcast(msg, txOptions); + var txResult = await _tx.Broadcast(msg, txOptions); + return new SingleSecretTx(txResult); } /// @@ -33,9 +27,10 @@ public async Task Deposit(MsgDeposit msg, TxOptions? txOptions = null) /// /// /// - public async Task SubmitProposal(MsgSubmitProposal msg, TxOptions? txOptions = null) + public async Task> SubmitProposal(Cosmos.Gov.V1Beta1.MsgSubmitProposal msg, TxOptions? txOptions = null) { - return await _tx.Broadcast(msg, txOptions); + var txResult = await _tx.Broadcast(msg, txOptions); + return new SingleSecretTx(txResult); } /// @@ -44,9 +39,10 @@ public async Task SubmitProposal(MsgSubmitProposal msg, TxOptions? txO /// /// /// - public async Task Vote(MsgVote msg, TxOptions? txOptions = null) + public async Task> Vote(Cosmos.Gov.V1Beta1.MsgVote msg, TxOptions? txOptions = null) { - return await _tx.Broadcast(msg, txOptions); + var txResult = await _tx.Broadcast(msg, txOptions); + return new SingleSecretTx(txResult); } /// @@ -55,9 +51,10 @@ public async Task Vote(MsgVote msg, TxOptions? txOptions = null) /// /// /// - public async Task VoteWeighted(MsgVoteWeighted msg, TxOptions? txOptions = null) + public async Task> VoteWeighted(Cosmos.Gov.V1Beta1.MsgVoteWeighted msg, TxOptions? txOptions = null) { - return await _tx.Broadcast(msg, txOptions); + var txResult = await _tx.Broadcast(msg, txOptions); + return new SingleSecretTx(txResult); } } diff --git a/src/Tx/IBC_Channel.Msg.cs b/src/Tx/IBC_Channel.Msg.cs index 8ed8be18..6bdc992c 100644 --- a/src/Tx/IBC_Channel.Msg.cs +++ b/src/Tx/IBC_Channel.Msg.cs @@ -7,11 +7,11 @@ public class MsgRecvPacket : MsgBase { public override string MsgType { get; } = MsgGrantAuthorization.MsgRecvPacket; - public override Task ToProto(SecretEncryptionUtils utils) + public override Task ToProto() { throw new NotImplementedException("MsgSubmitEvidence ToAmino is not implemented."); } - public override Task ToAmino(SecretEncryptionUtils utils) + public override Task ToAmino() { throw new NotImplementedException("MsgSubmitEvidence ToProto is not implemented."); } @@ -24,11 +24,11 @@ public class MsgTimeout : MsgBase { public override string MsgType { get; } = MsgGrantAuthorization.MsgTimeout; - public override Task ToProto(SecretEncryptionUtils utils) + public override Task ToProto() { throw new NotImplementedException("MsgTimeout ToAmino is not implemented."); } - public override Task ToAmino(SecretEncryptionUtils utils) + public override Task ToAmino() { throw new NotImplementedException("MsgTimeout ToProto is not implemented."); } @@ -41,11 +41,11 @@ public class MsgTimeoutOnClose : MsgBase { public override string MsgType { get; } = MsgGrantAuthorization.MsgTimeoutOnClose; - public override Task ToProto(SecretEncryptionUtils utils) + public override Task ToProto() { throw new NotImplementedException("MsgTimeoutOnClose ToAmino is not implemented."); } - public override Task ToAmino(SecretEncryptionUtils utils) + public override Task ToAmino() { throw new NotImplementedException("MsgTimeoutOnClose ToProto is not implemented."); } @@ -58,11 +58,11 @@ public class MsgChannelOpenInit : MsgBase { public override string MsgType { get; } = MsgGrantAuthorization.MsgChannelOpenInit; - public override Task ToProto(SecretEncryptionUtils utils) + public override Task ToProto() { throw new NotImplementedException("MsgChannelOpenInit ToAmino is not implemented."); } - public override Task ToAmino(SecretEncryptionUtils utils) + public override Task ToAmino() { throw new NotImplementedException("MsgChannelOpenInit ToProto is not implemented."); } @@ -75,11 +75,11 @@ public class MsgAcknowledgement : MsgBase { public override string MsgType { get; } = MsgGrantAuthorization.MsgAcknowledgement; - public override Task ToProto(SecretEncryptionUtils utils) + public override Task ToProto() { throw new NotImplementedException("MsgAcknowledgement ToAmino is not implemented."); } - public override Task ToAmino(SecretEncryptionUtils utils) + public override Task ToAmino() { throw new NotImplementedException("MsgAcknowledgement ToProto is not implemented."); } @@ -92,11 +92,11 @@ public class MsgChannelOpenTry : MsgBase { public override string MsgType { get; } = MsgGrantAuthorization.MsgChannelOpenTry; - public override Task ToProto(SecretEncryptionUtils utils) + public override Task ToProto() { throw new NotImplementedException("MsgChannelOpenTry ToAmino is not implemented."); } - public override Task ToAmino(SecretEncryptionUtils utils) + public override Task ToAmino() { throw new NotImplementedException("MsgChannelOpenTry ToProto is not implemented."); } @@ -109,11 +109,11 @@ public class MsgChannelOpenAck : MsgBase { public override string MsgType { get; } = MsgGrantAuthorization.MsgChannelOpenAck; - public override Task ToProto(SecretEncryptionUtils utils) + public override Task ToProto() { throw new NotImplementedException("MsgChannelOpenAck ToAmino is not implemented."); } - public override Task ToAmino(SecretEncryptionUtils utils) + public override Task ToAmino() { throw new NotImplementedException("MsgChannelOpenAck ToProto is not implemented."); } @@ -126,11 +126,11 @@ public class MsgChannelOpenConfirm : MsgBase { public override string MsgType { get; } = MsgGrantAuthorization.MsgChannelOpenConfirm; - public override Task ToProto(SecretEncryptionUtils utils) + public override Task ToProto() { throw new NotImplementedException("MsgChannelOpenConfirm ToAmino is not implemented."); } - public override Task ToAmino(SecretEncryptionUtils utils) + public override Task ToAmino() { throw new NotImplementedException("MsgChannelOpenConfirm ToProto is not implemented."); } @@ -143,11 +143,11 @@ public class MsgChannelCloseInit : MsgBase { public override string MsgType { get; } = MsgGrantAuthorization.MsgChannelCloseInit; - public override Task ToProto(SecretEncryptionUtils utils) + public override Task ToProto() { throw new NotImplementedException("MsgChannelCloseInit ToAmino is not implemented."); } - public override Task ToAmino(SecretEncryptionUtils utils) + public override Task ToAmino() { throw new NotImplementedException("MsgChannelCloseInit ToProto is not implemented."); } @@ -160,11 +160,11 @@ public class MsgChannelCloseConfirm : MsgBase { public override string MsgType { get; } = MsgGrantAuthorization.MsgChannelCloseConfirm; - public override Task ToProto(SecretEncryptionUtils utils) + public override Task ToProto() { throw new NotImplementedException("MsgChannelCloseConfirm ToAmino is not implemented."); } - public override Task ToAmino(SecretEncryptionUtils utils) + public override Task ToAmino() { throw new NotImplementedException("MsgChannelCloseConfirm ToProto is not implemented."); } diff --git a/src/Tx/IBC_ChannelTx.cs b/src/Tx/IBC_ChannelTx.cs new file mode 100644 index 00000000..349db084 --- /dev/null +++ b/src/Tx/IBC_ChannelTx.cs @@ -0,0 +1,73 @@ +namespace SecretNET.Tx; + +public class IbcChannelTx +{ + private TxClient _tx; + + internal IbcChannelTx(TxClient tx) + { + _tx = tx; + } + + public async Task> ChannelOpenInit(Ibc.Core.Channel.V1.MsgChannelOpenInit msg, TxOptions? txOptions = null) + { + var txResult = await _tx.Broadcast(msg, txOptions); + return new SingleSecretTx(txResult); + } + + public async Task> ChannelOpenTry(Ibc.Core.Channel.V1.MsgChannelOpenTry msg, TxOptions? txOptions = null) + { + var txResult = await _tx.Broadcast(msg, txOptions); + return new SingleSecretTx(txResult); + } + + public async Task> ChannelOpenAck(Ibc.Core.Channel.V1.MsgChannelOpenAck msg, TxOptions? txOptions = null) + { + var txResult = await _tx.Broadcast(msg, txOptions); + return new SingleSecretTx(txResult); + } + + public async Task> ChannelOpenConfirm(Ibc.Core.Channel.V1.MsgChannelOpenConfirm msg, TxOptions? txOptions = null) + { + var txResult = await _tx.Broadcast(msg, txOptions); + return new SingleSecretTx(txResult); + } + + public async Task> ChannelCloseInit(Ibc.Core.Channel.V1.MsgChannelCloseInit msg, TxOptions? txOptions = null) + { + var txResult = await _tx.Broadcast(msg, txOptions); + return new SingleSecretTx(txResult); + } + + public async Task> ChannelCloseConfirm(Ibc.Core.Channel.V1.MsgChannelCloseConfirm msg, TxOptions? txOptions = null) + { + var txResult = await _tx.Broadcast(msg, txOptions); + return new SingleSecretTx(txResult); + } + + public async Task> RecvPacket(Ibc.Core.Channel.V1.MsgRecvPacket msg, TxOptions? txOptions = null) + { + var txResult = await _tx.Broadcast(msg, txOptions); + return new SingleSecretTx(txResult); + } + + public async Task> Timeout(Ibc.Core.Channel.V1.MsgTimeout msg, TxOptions? txOptions = null) + { + var txResult = await _tx.Broadcast(msg, txOptions); + return new SingleSecretTx(txResult); + } + + public async Task> TimeoutOnClose(Ibc.Core.Channel.V1.MsgTimeoutOnClose msg, TxOptions? txOptions = null) + { + var txResult = await _tx.Broadcast(msg, txOptions); + return new SingleSecretTx(txResult); + } + + public async Task> Acknowledgement(Ibc.Core.Channel.V1.MsgAcknowledgement msg, TxOptions? txOptions = null) + { + var txResult = await _tx.Broadcast(msg, txOptions); + return new SingleSecretTx(txResult); + } + +} + \ No newline at end of file diff --git a/src/Tx/IBC_Client.Msg.cs b/src/Tx/IBC_Client.Msg.cs index f3af3b44..8855c2bf 100644 --- a/src/Tx/IBC_Client.Msg.cs +++ b/src/Tx/IBC_Client.Msg.cs @@ -7,12 +7,12 @@ public class MsgUpgradeClient : MsgBase { public override string MsgType { get; } = MsgGrantAuthorization.MsgUpgradeClient; - public override Task ToProto(SecretEncryptionUtils utils) + public override Task ToProto() { throw new NotImplementedException("MsgUpgradeClient ToAmino is not implemented."); } - public override Task ToAmino(SecretEncryptionUtils utils) + public override Task ToAmino() { throw new NotImplementedException("MsgUpgradeClient ToProto is not implemented."); } @@ -25,11 +25,11 @@ public class MsgSubmitMisbehaviour : MsgBase { public override string MsgType { get; } = MsgGrantAuthorization.MsgSubmitMisbehaviour; - public override Task ToProto(SecretEncryptionUtils utils) + public override Task ToProto() { throw new NotImplementedException("MsgSubmitMisbehaviour ToAmino is not implemented."); } - public override Task ToAmino(SecretEncryptionUtils utils) + public override Task ToAmino() { throw new NotImplementedException("MsgSubmitMisbehaviour ToProto is not implemented."); } @@ -42,11 +42,11 @@ public class MsgCreateClient : MsgBase { public override string MsgType { get; } = MsgGrantAuthorization.MsgCreateClient; - public override Task ToProto(SecretEncryptionUtils utils) + public override Task ToProto() { throw new NotImplementedException("MsgCreateClient ToAmino is not implemented."); } - public override Task ToAmino(SecretEncryptionUtils utils) + public override Task ToAmino() { throw new NotImplementedException("MsgCreateClient ToProto is not implemented."); } diff --git a/src/Tx/IBC_ClientTx.cs b/src/Tx/IBC_ClientTx.cs new file mode 100644 index 00000000..f6d26db6 --- /dev/null +++ b/src/Tx/IBC_ClientTx.cs @@ -0,0 +1,38 @@ +namespace SecretNET.Tx; + +public class IbcClientTx +{ + private TxClient _tx; + + internal IbcClientTx(TxClient tx) + { + _tx = tx; + } + + public async Task> CreateClient(Ibc.Core.Client.V1.MsgCreateClient msg, TxOptions? txOptions = null) + { + var txResult = await _tx.Broadcast(msg, txOptions); + return new SingleSecretTx(txResult); + } + + + public async Task> UpdateClient(Ibc.Core.Client.V1.MsgUpdateClient msg, TxOptions? txOptions = null) + { + var txResult = await _tx.Broadcast(msg, txOptions); + return new SingleSecretTx(txResult); + } + + public async Task> UpgradeClient(Ibc.Core.Client.V1.MsgUpgradeClient msg, TxOptions? txOptions = null) + { + var txResult = await _tx.Broadcast(msg, txOptions); + return new SingleSecretTx(txResult); + } + + public async Task> SubmitMisbehaviour(Ibc.Core.Client.V1.MsgSubmitMisbehaviour msg, TxOptions? txOptions = null) + { + var txResult = await _tx.Broadcast(msg, txOptions); + return new SingleSecretTx(txResult); + } + +} + \ No newline at end of file diff --git a/src/Tx/IBC_Connection.Msg.cs b/src/Tx/IBC_Connection.Msg.cs index c8fce623..9e618ee1 100644 --- a/src/Tx/IBC_Connection.Msg.cs +++ b/src/Tx/IBC_Connection.Msg.cs @@ -7,11 +7,11 @@ public class MsgConnectionOpenAck : MsgBase { public override string MsgType { get; } = MsgGrantAuthorization.MsgConnectionOpenAck; - public override Task ToProto(SecretEncryptionUtils utils) + public override Task ToProto() { throw new NotImplementedException("MsgConnectionOpenAck ToAmino is not implemented."); } - public override Task ToAmino(SecretEncryptionUtils utils) + public override Task ToAmino() { throw new NotImplementedException("MsgConnectionOpenAck ToProto is not implemented."); } @@ -24,11 +24,11 @@ public class MsgConnectionOpenConfirm : MsgBase { public override string MsgType { get; } = MsgGrantAuthorization.MsgConnectionOpenConfirm; - public override Task ToProto(SecretEncryptionUtils utils) + public override Task ToProto() { throw new NotImplementedException("MsgConnectionOpenConfirm ToAmino is not implemented."); } - public override Task ToAmino(SecretEncryptionUtils utils) + public override Task ToAmino() { throw new NotImplementedException("MsgConnectionOpenConfirm ToProto is not implemented."); } diff --git a/src/Tx/IBC_ConnectionTx.cs b/src/Tx/IBC_ConnectionTx.cs new file mode 100644 index 00000000..30ccff04 --- /dev/null +++ b/src/Tx/IBC_ConnectionTx.cs @@ -0,0 +1,38 @@ +namespace SecretNET.Tx; + +public class IbcConnectionTx +{ + private TxClient _tx; + + internal IbcConnectionTx(TxClient tx) + { + _tx = tx; + } + + public async Task> ConnectionOpenInit(Ibc.Core.Connection.V1.MsgConnectionOpenInit msg, TxOptions? txOptions = null) + { + var txResult = await _tx.Broadcast(msg, txOptions); + return new SingleSecretTx(txResult); + } + + + public async Task> ConnectionOpenTry(Ibc.Core.Connection.V1.MsgConnectionOpenTry msg, TxOptions? txOptions = null) + { + var txResult = await _tx.Broadcast(msg, txOptions); + return new SingleSecretTx(txResult); + } + + public async Task> ConnectionOpenAck(Ibc.Core.Connection.V1.MsgConnectionOpenAck msg, TxOptions? txOptions = null) + { + var txResult = await _tx.Broadcast(msg, txOptions); + return new SingleSecretTx(txResult); + } + + public async Task> ConnectionOpenConfirm(Ibc.Core.Connection.V1.MsgConnectionOpenConfirm msg, TxOptions? txOptions = null) + { + var txResult = await _tx.Broadcast(msg, txOptions); + return new SingleSecretTx(txResult); + } + +} + \ No newline at end of file diff --git a/src/Tx/IBC_Transfer.Msg.cs b/src/Tx/IBC_Transfer.Msg.cs index c3f411cb..537c5a03 100644 --- a/src/Tx/IBC_Transfer.Msg.cs +++ b/src/Tx/IBC_Transfer.Msg.cs @@ -71,7 +71,7 @@ public MsgTransfer(string sourcePort, string sourceChannel, Coin token, string s TimeoutTimestampSec = timeoutTimestampSec; } - public override async Task ToProto(SecretEncryptionUtils utils) + public override async Task ToProto() { var msgTransfer = new Ibc.Applications.Transfer.V1.MsgTransfer() { @@ -87,7 +87,7 @@ public override async Task ToProto(SecretEncryptionUtils utils) return msgTransfer; } - public override Task ToAmino(SecretEncryptionUtils utils) + public override Task ToAmino() { throw new NotImplementedException("MsgTransfer ToAmino is not implemented."); } diff --git a/src/Tx/IBC_TransferTx.Simulate.cs b/src/Tx/IBC_TransferTx.Simulate.cs deleted file mode 100644 index 67755039..00000000 --- a/src/Tx/IBC_TransferTx.Simulate.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace SecretNET.Tx; - -public class IbcTransferTxSimulate -{ - private TxClient _tx; - - internal IbcTransferTxSimulate(TxClient tx) - { - _tx = tx; - } - - /// - /// Simulates "MsgTransfer defines a msg to transfer fungible tokens (i.e Coins) between ICS20 enabled chains". - /// See ICS Spec here: - /// https://github.com/cosmos/ics/tree/master/spec/ics-020-fungible-token-transfer#data-structures - /// - /// - /// - /// - public async Task Transfer(MsgTransfer msg, TxOptions? txOptions = null) - { - return await _tx.Simulate(msg, txOptions); - } - -} - \ No newline at end of file diff --git a/src/Tx/IBC_TransferTx.cs b/src/Tx/IBC_TransferTx.cs index b03caee9..0f3cf955 100644 --- a/src/Tx/IBC_TransferTx.cs +++ b/src/Tx/IBC_TransferTx.cs @@ -3,17 +3,10 @@ public class IbcTransferTx { private TxClient _tx; - private IbcTransferTxSimulate _txSimulte; internal IbcTransferTx(TxClient tx) { _tx = tx; - _txSimulte = new IbcTransferTxSimulate(tx); - } - - public IbcTransferTxSimulate Simulate - { - get { return _txSimulte; } } /// @@ -24,9 +17,10 @@ public IbcTransferTxSimulate Simulate /// /// /// - public async Task Transfer(MsgTransfer msg, TxOptions? txOptions = null) + public async Task> Transfer(Ibc.Applications.Transfer.V1.MsgTransfer msg, TxOptions? txOptions = null) { - return await _tx.Broadcast(msg, txOptions); + var txResult = await _tx.Broadcast(msg, txOptions); + return new SingleSecretTx(txResult); } } diff --git a/src/Tx/Msg.cs b/src/Tx/Msg.cs index 6be90d44..f1d281e2 100644 --- a/src/Tx/Msg.cs +++ b/src/Tx/Msg.cs @@ -5,8 +5,58 @@ public abstract class MsgBase { public abstract string MsgType { get; } - public abstract Task ToProto(SecretEncryptionUtils utils); + public abstract Task ToProto(); - public abstract Task ToAmino(SecretEncryptionUtils utils); + public abstract Task ToAmino(); } + +/// +/// Message wrapper for all IMessage / Gprc Messages. +/// Implements the +/// +/// +public class Msg : MsgBase +{ + public override string MsgType { get; } + + public IMessage GrpcMsg { get; set; } + + public AminoMsg AminoMsg { get; set; } + + + public Msg(IMessage grpcMsg, AminoMsg aminoMsg = null) + { + GrpcMsg = grpcMsg; + AminoMsg = aminoMsg; + if (GrpcMsg != null) + { + MsgType = grpcMsg?.Descriptor?.FullName; + } + } + + /// + /// Returns the IMessage / Gprc Message + /// + /// Task<IMessage>. + /// GrpcMsg is null / not available + public override Task ToProto() + { + if (GrpcMsg != null) + { + return Task.FromResult(GrpcMsg); + } + throw new MissingMemberException("GrpcMsg is null / not available"); + } + + public override Task ToAmino() + { + if (AminoMsg != null) + { + return Task.FromResult(AminoMsg); + } + throw new MissingMemberException("AminoMsg is null / not available"); + } + + +} \ No newline at end of file diff --git a/src/Tx/MsgDecoderRegistry.cs b/src/Tx/MsgDecoderRegistry.cs index be1c4d64..b14f1d20 100644 --- a/src/Tx/MsgDecoderRegistry.cs +++ b/src/Tx/MsgDecoderRegistry.cs @@ -10,9 +10,87 @@ namespace SecretNET.Tx; internal static class MsgDecoderRegistry { internal static readonly ConcurrentDictionary> Registry = new ConcurrentDictionary>(); + internal static readonly ConcurrentDictionary> TypeRegistry = new ConcurrentDictionary>(); static MsgDecoderRegistry() { + + #region Response message by type => T.Parser.ParseFrom + + // Cosmos.Authz.V1Beta1 + TypeRegistry.TryAdd(typeof(Cosmos.Authz.V1Beta1.MsgGrantResponse), Cosmos.Authz.V1Beta1.MsgGrantResponse.Parser.ParseFrom); + TypeRegistry.TryAdd(typeof(Cosmos.Authz.V1Beta1.MsgExecResponse), Cosmos.Authz.V1Beta1.MsgExecResponse.Parser.ParseFrom); + TypeRegistry.TryAdd(typeof(Cosmos.Authz.V1Beta1.MsgRevokeResponse), Cosmos.Authz.V1Beta1.MsgRevokeResponse.Parser.ParseFrom); + + // Cosmos.Bank.V1Beta1 + TypeRegistry.TryAdd(typeof(Cosmos.Bank.V1Beta1.MsgSendResponse), Cosmos.Bank.V1Beta1.MsgSendResponse.Parser.ParseFrom); + TypeRegistry.TryAdd(typeof(Cosmos.Bank.V1Beta1.MsgMultiSendResponse), Cosmos.Bank.V1Beta1.MsgMultiSendResponse.Parser.ParseFrom); + + // Secret.Compute.V1Beta1 + TypeRegistry.TryAdd(typeof(Secret.Compute.V1Beta1.MsgStoreCodeResponse), Secret.Compute.V1Beta1.MsgStoreCodeResponse.Parser.ParseFrom); + TypeRegistry.TryAdd(typeof(Secret.Compute.V1Beta1.MsgInstantiateContractResponse), Secret.Compute.V1Beta1.MsgInstantiateContractResponse.Parser.ParseFrom); + TypeRegistry.TryAdd(typeof(Secret.Compute.V1Beta1.MsgExecuteContractResponse), Secret.Compute.V1Beta1.MsgExecuteContractResponse.Parser.ParseFrom); + + // Cosmos.Crisis.V1Beta1 + TypeRegistry.TryAdd(typeof(Cosmos.Crisis.V1Beta1.MsgVerifyInvariantResponse), Cosmos.Crisis.V1Beta1.MsgVerifyInvariantResponse.Parser.ParseFrom); + + // Cosmos.Distribution.V1Beta1 + TypeRegistry.TryAdd(typeof(Cosmos.Distribution.V1Beta1.MsgSetWithdrawAddressResponse), Cosmos.Distribution.V1Beta1.MsgSetWithdrawAddressResponse.Parser.ParseFrom); + TypeRegistry.TryAdd(typeof(Cosmos.Distribution.V1Beta1.MsgWithdrawDelegatorRewardResponse), Cosmos.Distribution.V1Beta1.MsgWithdrawDelegatorRewardResponse.Parser.ParseFrom); + TypeRegistry.TryAdd(typeof(Cosmos.Distribution.V1Beta1.MsgWithdrawValidatorCommissionResponse), Cosmos.Distribution.V1Beta1.MsgWithdrawValidatorCommissionResponse.Parser.ParseFrom); + TypeRegistry.TryAdd(typeof(Cosmos.Distribution.V1Beta1.MsgFundCommunityPoolResponse), Cosmos.Distribution.V1Beta1.MsgFundCommunityPoolResponse.Parser.ParseFrom); + + // Cosmos.Evidence.V1Beta1 + TypeRegistry.TryAdd(typeof(Cosmos.Evidence.V1Beta1.MsgSubmitEvidenceResponse), Cosmos.Evidence.V1Beta1.MsgSubmitEvidenceResponse.Parser.ParseFrom); + + // Cosmos.Feegrant.V1Beta1 + TypeRegistry.TryAdd(typeof(Cosmos.Feegrant.V1Beta1.MsgGrantAllowanceResponse), Cosmos.Feegrant.V1Beta1.MsgGrantAllowanceResponse.Parser.ParseFrom); + TypeRegistry.TryAdd(typeof(Cosmos.Feegrant.V1Beta1.MsgRevokeAllowanceResponse), Cosmos.Feegrant.V1Beta1.MsgRevokeAllowanceResponse.Parser.ParseFrom); + + // Cosmos.Gov.V1Beta1 + TypeRegistry.TryAdd(typeof(Cosmos.Gov.V1Beta1.MsgSubmitProposalResponse), Cosmos.Gov.V1Beta1.MsgSubmitProposalResponse.Parser.ParseFrom); + TypeRegistry.TryAdd(typeof(Cosmos.Gov.V1Beta1.MsgVoteResponse), Cosmos.Gov.V1Beta1.MsgVoteResponse.Parser.ParseFrom); + TypeRegistry.TryAdd(typeof(Cosmos.Gov.V1Beta1.MsgVoteWeightedResponse), Cosmos.Gov.V1Beta1.MsgVoteWeightedResponse.Parser.ParseFrom); + TypeRegistry.TryAdd(typeof(Cosmos.Gov.V1Beta1.MsgDepositResponse), Cosmos.Gov.V1Beta1.MsgDepositResponse.Parser.ParseFrom); + + // Ibc.Core.Channel.V1 + TypeRegistry.TryAdd(typeof(Ibc.Core.Channel.V1.MsgRecvPacketResponse), Ibc.Core.Channel.V1.MsgRecvPacketResponse.Parser.ParseFrom); + TypeRegistry.TryAdd(typeof(Ibc.Core.Channel.V1.MsgTimeoutResponse), Ibc.Core.Channel.V1.MsgTimeoutResponse.Parser.ParseFrom); + TypeRegistry.TryAdd(typeof(Ibc.Core.Channel.V1.MsgTimeoutOnCloseResponse), Ibc.Core.Channel.V1.MsgTimeoutOnCloseResponse.Parser.ParseFrom); + TypeRegistry.TryAdd(typeof(Ibc.Core.Channel.V1.MsgChannelOpenInitResponse), Ibc.Core.Channel.V1.MsgChannelOpenInitResponse.Parser.ParseFrom); + TypeRegistry.TryAdd(typeof(Ibc.Core.Channel.V1.MsgAcknowledgementResponse), Ibc.Core.Channel.V1.MsgAcknowledgementResponse.Parser.ParseFrom); + TypeRegistry.TryAdd(typeof(Ibc.Core.Channel.V1.MsgChannelOpenTryResponse), Ibc.Core.Channel.V1.MsgChannelOpenTryResponse.Parser.ParseFrom); + TypeRegistry.TryAdd(typeof(Ibc.Core.Channel.V1.MsgChannelOpenAckResponse), Ibc.Core.Channel.V1.MsgChannelOpenAckResponse.Parser.ParseFrom); + TypeRegistry.TryAdd(typeof(Ibc.Core.Channel.V1.MsgChannelOpenConfirmResponse), Ibc.Core.Channel.V1.MsgChannelOpenConfirmResponse.Parser.ParseFrom); + TypeRegistry.TryAdd(typeof(Ibc.Core.Channel.V1.MsgChannelCloseInitResponse), Ibc.Core.Channel.V1.MsgChannelCloseInitResponse.Parser.ParseFrom); + TypeRegistry.TryAdd(typeof(Ibc.Core.Channel.V1.MsgChannelCloseConfirmResponse), Ibc.Core.Channel.V1.MsgChannelCloseConfirmResponse.Parser.ParseFrom); + + // Ibc.Core.Client.V1 + TypeRegistry.TryAdd(typeof(Ibc.Core.Client.V1.MsgUpdateClientResponse), Ibc.Core.Client.V1.MsgUpdateClientResponse.Parser.ParseFrom); + TypeRegistry.TryAdd(typeof(Ibc.Core.Client.V1.MsgUpgradeClientResponse), Ibc.Core.Client.V1.MsgUpgradeClientResponse.Parser.ParseFrom); + TypeRegistry.TryAdd(typeof(Ibc.Core.Client.V1.MsgSubmitMisbehaviourResponse), Ibc.Core.Client.V1.MsgSubmitMisbehaviourResponse.Parser.ParseFrom); + TypeRegistry.TryAdd(typeof(Ibc.Core.Client.V1.MsgCreateClientResponse), Ibc.Core.Client.V1.MsgCreateClientResponse.Parser.ParseFrom); + + // Ibc.Applications.Transfer.V1 + TypeRegistry.TryAdd(typeof(Ibc.Applications.Transfer.V1.MsgTransferResponse), Ibc.Applications.Transfer.V1.MsgTransferResponse.Parser.ParseFrom); + + // Cosmos.Slashing.V1Beta1 + TypeRegistry.TryAdd(typeof(Cosmos.Slashing.V1Beta1.MsgUnjailResponse), Cosmos.Slashing.V1Beta1.MsgUnjailResponse.Parser.ParseFrom); + + // Cosmos.Staking.V1Beta1 + TypeRegistry.TryAdd(typeof(Cosmos.Staking.V1Beta1.MsgCreateValidatorResponse), Cosmos.Staking.V1Beta1.MsgCreateValidatorResponse.Parser.ParseFrom); + TypeRegistry.TryAdd(typeof(Cosmos.Staking.V1Beta1.MsgEditValidatorResponse), Cosmos.Staking.V1Beta1.MsgEditValidatorResponse.Parser.ParseFrom); + TypeRegistry.TryAdd(typeof(Cosmos.Staking.V1Beta1.MsgDelegateResponse), Cosmos.Staking.V1Beta1.MsgDelegateResponse.Parser.ParseFrom); + TypeRegistry.TryAdd(typeof(Cosmos.Staking.V1Beta1.MsgBeginRedelegateResponse), Cosmos.Staking.V1Beta1.MsgBeginRedelegateResponse.Parser.ParseFrom); + TypeRegistry.TryAdd(typeof(Cosmos.Staking.V1Beta1.MsgUndelegateResponse), Cosmos.Staking.V1Beta1.MsgUndelegateResponse.Parser.ParseFrom); + + // Cosmos.Vesting.V1Beta1 + TypeRegistry.TryAdd(typeof(Cosmos.Vesting.V1Beta1.MsgCreateVestingAccountResponse), Cosmos.Vesting.V1Beta1.MsgCreateVestingAccountResponse.Parser.ParseFrom); + + #endregion + + #region Message by type name => any.Unpack + // Cosmos.Authz.V1Beta1 Registry.TryAdd(MsgGrantAuthorization.MsgGrant.ToLower(), (any) => any.Unpack()); Registry.TryAdd(MsgGrantAuthorization.MsgExec.ToLower(), (any) => any.Unpack()); @@ -86,12 +164,10 @@ static MsgDecoderRegistry() Registry.TryAdd(MsgTypeNames.StakeAuthorization.ToLower(), (any) => any.Unpack()); // Cosmos.Vesting.V1Beta1 - Registry.TryAdd(MsgTypeNames.MsgCreateVestingAccount.ToLower(), (any) => any.Unpack()); - + Registry.TryAdd(MsgTypeNames.MsgCreateVestingAccount.ToLower(), (any) => any.Unpack()); - + #endregion - } public static Func Get(string typeUrl) @@ -106,4 +182,16 @@ public static Func Get(string typeUrl) } return null; } + + public static Func Get(System.Type msgType) + { + if (msgType != null) + { + if (TypeRegistry.ContainsKey(msgType)) + { + return TypeRegistry[msgType]; + } + } + return null; + } } diff --git a/src/Tx/Registration.Msg.cs b/src/Tx/Registration.Msg.cs index 0e30e075..eeb66a1a 100644 --- a/src/Tx/Registration.Msg.cs +++ b/src/Tx/Registration.Msg.cs @@ -19,7 +19,7 @@ public RaAuthenticate(string sender, byte[] certificate) Certificate = certificate; } - public override async Task ToProto(SecretEncryptionUtils utils) + public override async Task ToProto() { var msgTransfer = new Secret.Registration.V1Beta1.RaAuthenticate() { @@ -29,7 +29,7 @@ public override async Task ToProto(SecretEncryptionUtils utils) return msgTransfer; } - public override async Task ToAmino(SecretEncryptionUtils utils) + public override async Task ToAmino() { var aminoMsg = new AminoMsg("reg/authenticate"); // order of properties must be sorted for amino signing!! diff --git a/src/Tx/SecretTx.cs b/src/Tx/SecretTx.cs index b7019bc8..5d3a549e 100644 --- a/src/Tx/SecretTx.cs +++ b/src/Tx/SecretTx.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +using Tendermint.Abci; namespace SecretNET.Tx { @@ -133,12 +134,33 @@ public SecretTx(Exception ex, string txHash) /// T. public T GetResponseMsg(int msgIndex = 0) { - var msgData = GetResponseData(msgIndex); - if (msgData != null) + T response = default(T); + try + { + var msgData = GetResponseData(0); + if (msgData != null) + { + if (typeof(IMessage).IsAssignableFrom(typeof(T))) + { + var decoder = MsgDecoderRegistry.Get(typeof(T)); + if (decoder != null) + { + response = (T)decoder(msgData); + } + } + else + { + var jsonData = Encoding.UTF8.GetString(msgData); + response = JsonConvert.DeserializeObject(jsonData); + } + } + } + catch (Exception ex) { - return JsonConvert.DeserializeObject(Encoding.UTF8.GetString(msgData)); + Exceptions = Exceptions != null ? Exceptions : new List(); + Exceptions.Add(ex); } - return default; + return response; } /// @@ -224,8 +246,19 @@ private void ParseData() var msgData = GetResponseData(0); if (msgData != null) { - var jsonData = Encoding.UTF8.GetString(msgData); - Response = JsonConvert.DeserializeObject(jsonData); + if (typeof(IMessage).IsAssignableFrom(typeof(T))) + { + var decoder = MsgDecoderRegistry.Get(typeof(T)); + if (decoder != null) + { + Response = (T)decoder(msgData); + } + } + else + { + var jsonData = Encoding.UTF8.GetString(msgData); + Response = JsonConvert.DeserializeObject(jsonData); + } } } catch (Exception ex) @@ -234,6 +267,7 @@ private void ParseData() Exceptions.Add(ex); } } - } + + } diff --git a/src/Tx/Slashing.Msg.cs b/src/Tx/Slashing.Msg.cs index 82e5c023..4f1c76d7 100644 --- a/src/Tx/Slashing.Msg.cs +++ b/src/Tx/Slashing.Msg.cs @@ -16,7 +16,7 @@ public MsgUnjail(string validatorAddress) ValidatorAddress = validatorAddress; } - public override async Task ToProto(SecretEncryptionUtils utils) + public override async Task ToProto() { var msgUnjail = new Cosmos.Slashing.V1Beta1.MsgUnjail() { @@ -25,7 +25,7 @@ public override async Task ToProto(SecretEncryptionUtils utils) return msgUnjail; } - public override async Task ToAmino(SecretEncryptionUtils utils) + public override async Task ToAmino() { var aminoMsg = new AminoMsg("cosmos-sdk/MsgUnjail"); // order of properties must be sorted for amino signing!! diff --git a/src/Tx/SlashingTx.Simulate.cs b/src/Tx/SlashingTx.Simulate.cs deleted file mode 100644 index 1be43652..00000000 --- a/src/Tx/SlashingTx.Simulate.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace SecretNET.Tx; - -public class SlashingTxSimulate -{ - private TxClient _tx; - - internal SlashingTxSimulate(TxClient tx) - { - _tx = tx; - } - - /// - /// Simulates "MsgUnjail defines a message to release a validator from jail". - /// - /// - /// - /// - public async Task Unjail(MsgUnjail msg, TxOptions? txOptions = null) - { - return await _tx.Simulate(msg, txOptions); - } - -} - \ No newline at end of file diff --git a/src/Tx/SlashingTx.cs b/src/Tx/SlashingTx.cs index 0b4afc20..3562b3cb 100644 --- a/src/Tx/SlashingTx.cs +++ b/src/Tx/SlashingTx.cs @@ -3,17 +3,10 @@ public class SlashingTx { private TxClient _tx; - private SlashingTxSimulate _txSimulte; internal SlashingTx(TxClient tx) { _tx = tx; - _txSimulte = new SlashingTxSimulate(tx); - } - - public SlashingTxSimulate Simulate - { - get { return _txSimulte; } } /// @@ -22,9 +15,10 @@ public SlashingTxSimulate Simulate /// /// /// - public async Task Unjail(MsgUnjail msg, TxOptions? txOptions = null) + public async Task> Unjail(Cosmos.Slashing.V1Beta1.MsgUnjail msg, TxOptions? txOptions = null) { - return await _tx.Broadcast(msg, txOptions); + var txResult = await _tx.Broadcast(msg, txOptions); + return new SingleSecretTx(txResult); } } diff --git a/src/Tx/Staking.Msg.cs b/src/Tx/Staking.Msg.cs index 694d5df0..06f1371a 100644 --- a/src/Tx/Staking.Msg.cs +++ b/src/Tx/Staking.Msg.cs @@ -57,7 +57,7 @@ public MsgCreateValidator( InitialDelegation = initialDelegation; } - public override async Task ToProto(SecretEncryptionUtils utils) + public override async Task ToProto() { var msgCreateValidator = new Cosmos.Staking.V1Beta1.MsgCreateValidator() { @@ -81,7 +81,7 @@ public override async Task ToProto(SecretEncryptionUtils utils) return msgCreateValidator; } - public override async Task ToAmino(SecretEncryptionUtils utils) + public override async Task ToAmino() { throw new NotImplementedException("MsgCreateValidator ToAmino is not implemented."); } @@ -124,7 +124,7 @@ public MsgEditValidator(string validatorAddress, string minSelfDelegation, CommissionRate = commissionRate; } - public override async Task ToProto(SecretEncryptionUtils utils) + public override async Task ToProto() { var msgEditValidator = new Cosmos.Staking.V1Beta1.MsgEditValidator() { @@ -136,7 +136,7 @@ public override async Task ToProto(SecretEncryptionUtils utils) return msgEditValidator; } - public override async Task ToAmino(SecretEncryptionUtils utils) + public override async Task ToAmino() { throw new NotImplementedException("MsgEditValidator ToAmino is not implemented."); } @@ -164,7 +164,7 @@ public MsgDelegate(string delegatorAddress, string validatorAddress, Coin amount Amount = amount; } - public override async Task ToProto(SecretEncryptionUtils utils) + public override async Task ToProto() { var msgDelegate = new Cosmos.Staking.V1Beta1.MsgDelegate() { @@ -178,7 +178,7 @@ public override async Task ToProto(SecretEncryptionUtils utils) return msgDelegate; } - public override async Task ToAmino(SecretEncryptionUtils utils) + public override async Task ToAmino() { throw new NotImplementedException("MsgDelegate ToAmino is not implemented."); } @@ -209,7 +209,7 @@ public MsgBeginRedelegate(string delegatorAddress, string validatorSrcAddress, s Amount = amount; } - public override async Task ToProto(SecretEncryptionUtils utils) + public override async Task ToProto() { var msgBeginRedelegate = new Cosmos.Staking.V1Beta1.MsgBeginRedelegate() { @@ -224,7 +224,7 @@ public override async Task ToProto(SecretEncryptionUtils utils) return msgBeginRedelegate; } - public override async Task ToAmino(SecretEncryptionUtils utils) + public override async Task ToAmino() { throw new NotImplementedException("MsgBeginRedelegate ToAmino is not implemented."); } @@ -252,7 +252,7 @@ public MsgUndelegate(string delegatorAddress, string validatorAddress, Coin amou Amount = amount; } - public override async Task ToProto(SecretEncryptionUtils utils) + public override async Task ToProto() { var msgUndelegate = new Cosmos.Staking.V1Beta1.MsgUndelegate() { @@ -266,7 +266,7 @@ public override async Task ToProto(SecretEncryptionUtils utils) return msgUndelegate; } - public override async Task ToAmino(SecretEncryptionUtils utils) + public override async Task ToAmino() { throw new NotImplementedException("MsgUndelegate ToAmino is not implemented."); } diff --git a/src/Tx/StakingTx.Simulate.cs b/src/Tx/StakingTx.Simulate.cs deleted file mode 100644 index 0ff0ebb0..00000000 --- a/src/Tx/StakingTx.Simulate.cs +++ /dev/null @@ -1,68 +0,0 @@ -namespace SecretNET.Tx; - -public class StakingTxSimulate -{ - private TxClient _tx; - - internal StakingTxSimulate(TxClient tx) - { - _tx = tx; - } - - /// - /// Simulates "MsgBeginRedelegate defines an SDK message for performing a redelegation of coins from a delegator and source validator to a destination validator". - /// - /// - /// - /// - public async Task BeginRedelegate(MsgBeginRedelegate msg, TxOptions? txOptions = null) - { - return await _tx.Simulate(msg, txOptions); - } - - /// - /// Simulates "MsgCreateValidator defines an SDK message for creating a new validator". - /// - /// - /// - /// - public async Task CreateValidator(MsgCreateValidator msg, TxOptions? txOptions = null) - { - return await _tx.Simulate(msg, txOptions); - } - - /// - /// Simulates "MsgDelegate defines an SDK message for performing a delegation of coins from a delegator to a validator". - /// - /// - /// - /// - public async Task Delegate (MsgDelegate msg, TxOptions? txOptions = null) - { - return await _tx.Simulate(msg, txOptions); - } - - /// - /// Simulates "MsgEditValidator defines an SDK message for editing an existing validator". - /// - /// - /// - /// - public async Task EditValidator(MsgEditValidator msg, TxOptions? txOptions = null) - { - return await _tx.Simulate(msg, txOptions); - } - - /// - /// Simulates "MsgUndelegate defines an SDK message for performing an undelegation from a delegate and a validator" - /// - /// - /// - /// - public async Task Undelegate(MsgUndelegate msg, TxOptions? txOptions = null) - { - return await _tx.Simulate(msg, txOptions); - } - -} - \ No newline at end of file diff --git a/src/Tx/StakingTx.cs b/src/Tx/StakingTx.cs index 5ac75152..19e7bd87 100644 --- a/src/Tx/StakingTx.cs +++ b/src/Tx/StakingTx.cs @@ -3,17 +3,10 @@ public class StakingTx { private TxClient _tx; - private StakingTxSimulate _txSimulte; internal StakingTx(TxClient tx) { _tx = tx; - _txSimulte = new StakingTxSimulate(tx); - } - - public StakingTxSimulate Simulate - { - get { return _txSimulte; } } /// @@ -22,9 +15,10 @@ public StakingTxSimulate Simulate /// /// /// - public async Task BeginRedelegate(MsgBeginRedelegate msg, TxOptions? txOptions = null) + public async Task> BeginRedelegate(Cosmos.Staking.V1Beta1.MsgBeginRedelegate msg, TxOptions? txOptions = null) { - return await _tx.Broadcast(msg, txOptions); + var txResult = await _tx.Broadcast(msg, txOptions); + return new SingleSecretTx(txResult); } /// @@ -33,9 +27,10 @@ public async Task BeginRedelegate(MsgBeginRedelegate msg, TxOptions? t /// /// /// - public async Task CreateValidator(MsgCreateValidator msg, TxOptions? txOptions = null) + public async Task> CreateValidator(Cosmos.Staking.V1Beta1.MsgCreateValidator msg, TxOptions? txOptions = null) { - return await _tx.Broadcast(msg, txOptions); + var txResult = await _tx.Broadcast(msg, txOptions); + return new SingleSecretTx(txResult); } /// @@ -44,9 +39,10 @@ public async Task CreateValidator(MsgCreateValidator msg, TxOptions? t /// /// /// - public async Task Delegate (MsgDelegate msg, TxOptions? txOptions = null) + public async Task> Delegate (Cosmos.Staking.V1Beta1.MsgDelegate msg, TxOptions? txOptions = null) { - return await _tx.Broadcast(msg, txOptions); + var txResult = await _tx.Broadcast(msg, txOptions); + return new SingleSecretTx(txResult); } /// @@ -55,9 +51,10 @@ public async Task Delegate (MsgDelegate msg, TxOptions? txOptions = nu /// /// /// - public async Task EditValidator(MsgEditValidator msg, TxOptions? txOptions = null) + public async Task> EditValidator(Cosmos.Staking.V1Beta1.MsgEditValidator msg, TxOptions? txOptions = null) { - return await _tx.Broadcast(msg, txOptions); + var txResult = await _tx.Broadcast(msg, txOptions); + return new SingleSecretTx(txResult); } /// @@ -66,9 +63,10 @@ public async Task EditValidator(MsgEditValidator msg, TxOptions? txOpt /// /// /// - public async Task Undelegate(MsgUndelegate msg, TxOptions? txOptions = null) + public async Task> Undelegate(Cosmos.Staking.V1Beta1.MsgUndelegate msg, TxOptions? txOptions = null) { - return await _tx.Broadcast(msg, txOptions); + var txResult = await _tx.Broadcast(msg, txOptions); + return new SingleSecretTx(txResult); } } diff --git a/src/Tx/TxClient.cs b/src/Tx/TxClient.cs index 1accd5f5..7ee164ac 100644 --- a/src/Tx/TxClient.cs +++ b/src/Tx/TxClient.cs @@ -1,23 +1,61 @@ -using Cosmos.Tx.V1Beta1; -using Cosmos.Base.Abci.V1Beta1; -using System.Text.RegularExpressions; -using System.Collections.Concurrent; -using Google.Protobuf; +using SecretNET.Query; namespace SecretNET.Tx; public class TxClient : GprcBase { + private Queries _queries; private Service.ServiceClient _txClient; - + + // Cosmos + private AuthzTx _authzTx; + private BankTx _bankTx; + private CrisisTx _crisisTx; + private DistributionTx _distributionTx; + private EvidenceTx _evidenceTx; + private FeegrantTx _feegrantTx; + private GovTx _govTx; + private SlashingTx _slashingTx; + private StakingTx _stakingTx; + private VestingTx _vestingTx; + + // IBC + private IbcChannelTx _ibcChannelTx; + private IbcClientTx _ibcClientTx; + private IbcConnectionTx _ibcConnectionTx; + private IbcTransferTx _ibcTransferTx; + + // Secret Network private ComputeTx _computeTx; - internal TxClient(ISecretNetworkClient secretNetworkClient, GrpcChannel grpcChannel, CallInvoker? grpcMessageInterceptor) : base(secretNetworkClient, grpcChannel, grpcMessageInterceptor) - { + + internal TxClient(ISecretNetworkClient secretNetworkClient, Queries queries, GrpcChannel grpcChannel, CallInvoker? grpcMessageInterceptor) : base(secretNetworkClient, grpcChannel, grpcMessageInterceptor) + { + _queries = queries; + + // Cosmos + _authzTx = new AuthzTx(this); + _bankTx = new BankTx(this); + _crisisTx = new CrisisTx(this); + _distributionTx = new DistributionTx(this); + _evidenceTx = new EvidenceTx(this); + _feegrantTx = new FeegrantTx(this); + _govTx = new GovTx(this); + _slashingTx = new SlashingTx(this); + _stakingTx = new StakingTx(this); ; + _vestingTx = new VestingTx(this); + + // IBC + _ibcChannelTx = new IbcChannelTx(this); + _ibcClientTx = new IbcClientTx(this); + _ibcConnectionTx = new IbcConnectionTx(this); + _ibcTransferTx = new IbcTransferTx(this); + + // Secret Network _computeTx = new ComputeTx(this); } - private Service.ServiceClient client + internal Service.ServiceClient ServiceClient { get { @@ -29,11 +67,87 @@ private Service.ServiceClient client } } + public AuthzTx Authz + { + get { return _authzTx; } + } + + public BankTx Bank + { + get { return _bankTx; } + } + public ComputeTx Compute { get { return _computeTx; } } + public CrisisTx Crisis + { + get { return _crisisTx; } + } + + public DistributionTx Distribution + { + get { return _distributionTx; } + } + + public EvidenceTx Evidence + { + get { return _evidenceTx; } + } + + public FeegrantTx Feegrant + { + get { return _feegrantTx; } + } + + public GovTx Gov + { + get { return _govTx; } + } + + public SlashingTx Slashing + { + get { return _slashingTx; } + } + + public StakingTx Staking + { + get { return _stakingTx; } + } + + public VestingTx Vesting + { + get { return _vestingTx; } + } + + public IbcChannelTx IbcChannel + { + get { return _ibcChannelTx; } + } + + public IbcClientTx IbcClient + { + get { return _ibcClientTx; } + } + + public IbcConnectionTx IbcConnection + { + get { return _ibcConnectionTx; } + } + + public IbcTransferTx IbcTransfer + { + get { return _ibcTransferTx; } + } + + /// + /// Simulates the specified Tx / message. + /// + /// The message. + /// The tx options. + /// SimulateResponse. public async Task Simulate(MsgBase message, TxOptions txOptions = null) { if (message is MsgExecuteContract && string.IsNullOrEmpty(((MsgExecuteContract)message).Sender)) @@ -43,6 +157,12 @@ public async Task Simulate(MsgBase message, TxOptions txOption return await Simulate(new MsgBase[] { message }, txOptions); } + /// + /// Simulates the specified Tx / messages. + /// + /// The messages. + /// The tx options. + /// SimulateResponse. public async Task Simulate(MsgBase[] messages, TxOptions txOptions = null) { foreach (var msg in messages) @@ -59,20 +179,50 @@ public async Task Simulate(MsgBase[] messages, TxOptions txOpt TxBytes = ByteString.CopyFrom(prepareResult) }; - var result = await client.SimulateAsync(request); + var result = await ServiceClient.SimulateAsync(request); return result; } + /// + /// Broadcasts the specified Tx / message. + /// + /// The message. + /// The tx options. + /// SecretTx. public async Task Broadcast(MsgBase message, TxOptions txOptions = null) { return await Broadcast(new MsgBase[] { message }, txOptions); } + public async Task Broadcast(IMessage message, TxOptions txOptions = null) + { + return await Broadcast(new MsgBase[] { new Msg(message) }, txOptions); + } + + /// + /// Broadcasts the specified Tx / message. + /// + /// + /// The message. + /// The tx options. + /// SingleSecretTx<T>. public async Task> Broadcast(MsgBase message, TxOptions txOptions = null) { return await Broadcast(new MsgBase[] { message }, txOptions); } + public async Task> Broadcast(IMessage message, TxOptions txOptions = null) + { + return await Broadcast(new MsgBase[] { new Msg(message) }, txOptions); + } + + /// + /// Broadcasts the specified Tx / messages. + /// + /// + /// The messages. + /// The tx options. + /// SingleSecretTx<T>. public async Task> Broadcast(MsgBase[] messages, TxOptions txOptions = null) { var result = await Broadcast(messages, txOptions); @@ -83,6 +233,22 @@ public async Task> Broadcast(MsgBase[] messages, TxOptions return null; } + public async Task> Broadcast(IMessage[] messages, TxOptions txOptions = null) + { + var msgBaseList = new List(); + foreach(IMessage msg in messages) + { + msgBaseList.Add(new Msg(msg)); + } + return await Broadcast(msgBaseList.ToArray(), txOptions); + } + + /// + /// Broadcasts the specified Tx / messages. + /// + /// The messages. + /// The tx options. + /// SecretTx. public async Task Broadcast(MsgBase[] messages, TxOptions txOptions = null) { txOptions = txOptions != null ? txOptions : new TxOptions(); @@ -118,7 +284,7 @@ private async Task BroadcastTx(byte[] TxBytes, TxOptions txOptions) try { - broadcastResponse = await client.BroadcastTxAsync(request); + broadcastResponse = await ServiceClient.BroadcastTxAsync(request); } catch (Exception ex) { @@ -130,13 +296,13 @@ private async Task BroadcastTx(byte[] TxBytes, TxOptions txOptions) if (!isBroadcastTimedOut) { - return await DecodeTxResponse(broadcastResponse?.TxResponse); + return await _queries.DecodeTxResponse(broadcastResponse?.TxResponse); } } else if (mode == BroadcastMode.Sync) { - broadcastResponse = await client.BroadcastTxAsync(request); + broadcastResponse = await ServiceClient.BroadcastTxAsync(request); // Check for errors if (broadcastResponse.TxResponse.Code > 0) @@ -162,7 +328,7 @@ private async Task BroadcastTx(byte[] TxBytes, TxOptions txOptions) } else if (mode == BroadcastMode.Async) { - broadcastResponse = await client.BroadcastTxAsync(request); + broadcastResponse = await ServiceClient.BroadcastTxAsync(request); } else { @@ -186,7 +352,7 @@ private async Task BroadcastTx(byte[] TxBytes, TxOptions txOptions) SecretTx txResult = null; Func getTx = async (hash) => { - txResult = await GetTx(hash); + txResult = await _queries.GetTx(hash); }; await Task.WhenAny(getTx(txHash), Task.Delay(TimeSpan.FromMilliseconds(checkIntervalMs))); @@ -203,308 +369,4 @@ private async Task BroadcastTx(byte[] TxBytes, TxOptions txOptions) } } - private readonly Regex _errorMessageRegEx = new Regex("; message index: (?\\d+):(?: dispatch: submessages:)* encrypted: (?.+?): (?:instantiate|execute|query) contract failed", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline); - - /// - /// Gets the tx. - /// - /// The hash. - /// if set to true the client tries to decrypt the tx data (works only if the tx was created in the same session / client instance or if the same CreateClientOptions.EncryptionSeed is used). - /// SecretTx. - public async Task GetTx(string hash, bool tryToDecrypt = true) - { - var result = await TxsQuery($"tx.hash='{hash}'", tryToDecrypt); - return result?[0]; - } - - - /// - /// TXSs the query. - /// - /// The query. - /// if set to true the client tries to decrypt the tx data (works only if the tx was created in the same session / client instance or if the same CreateClientOptions.EncryptionSeed is used). - /// SecretTx[]. - public async Task TxsQuery(string query, bool tryToDecrypt = false) - { - if (!String.IsNullOrWhiteSpace(query)) - { - GetTxsEventRequest request = new GetTxsEventRequest(); - request.Events.Add(query.Split(" AND ").Select(q => q.Trim()).ToList()); - - var result = await GetTxsEvent(request, tryToDecrypt); - return result; - } - return null; - } - - /// - /// GetTxsEvent fetches txs by event. - /// - /// The request. - /// if set to true the client tries to decrypt the tx data (works only if the tx was created in the same session / client instance or if the same CreateClientOptions.EncryptionSeed is used). - /// GetTxsEventResponse. - public async Task GetTxsEvent(GetTxsEventRequest request, bool tryToDecrypt = false) - { - var result = await client.GetTxsEventAsync(request); - - var decodedResponses = await this.DecodeTxResponses(result?.TxResponses?.ToArray(), tryToDecrypt); - return decodedResponses; - } - - - internal async Task DecodeTxResponse(TxResponse txResponse, bool tryToDecrypt = false) - { - if (txResponse == null) return null; - - var result = await DecodeTxResponses(new TxResponse[] { txResponse }, tryToDecrypt); - return result?[0]; - } - - internal async Task DecodeTxResponses(TxResponse[] txResponses, bool tryToDecrypt = false) - { - if (txResponses == null || txResponses.Length == 0) return null; - var result = new ConcurrentBag(); - - await Parallel.ForEachAsync(txResponses, async (txResponse, cancellationToken) => - { - var decodedTx = txResponse.Tx.Unpack(); - - var nonces = new ComputeMsgToNonce(); - - // Decoded input tx messages - for (var i = 0; i < decodedTx?.Body?.Messages?.Count; i++) - { - var rawMsg = decodedTx.Body.Messages[i]; - - var messageDecoder = MsgDecoderRegistry.Get(rawMsg.TypeUrl); - if (messageDecoder == null) continue; - - var msg = messageDecoder(rawMsg); - - if (tryToDecrypt) - { - // Check if the message needs decryption - byte[] inputMsgBytes = null; - if (rawMsg.TypeUrl.IsProtoType(MsgGrantAuthorization.MsgInstantiateContract)) - { - inputMsgBytes = ((Secret.Compute.V1Beta1.MsgInstantiateContract)msg).InitMsg.Span.ToArray(); - } - else if (rawMsg.TypeUrl.IsProtoType(MsgGrantAuthorization.MsgExecuteContract)) - { - inputMsgBytes = ((Secret.Compute.V1Beta1.MsgExecuteContract)msg).Msg.Span.ToArray(); - } - - if (inputMsgBytes != null && inputMsgBytes.Length > 0) - { - // Encrypted, try to decrypt - try - { - var nonce = new ArraySegment(inputMsgBytes, 0, 32).ToArray(); - //Console.WriteLine("DecodeTxResponses nonce: " + nonce.ToHexString()); - - var accountPubkey = new ArraySegment(inputMsgBytes, 32, 32).ToArray(); // unused in decryption - var ciphertext = new ArraySegment(inputMsgBytes, 64, inputMsgBytes.Length - 64).ToArray(); - var plaintext = await Encryption.Decrypt(ciphertext, nonce); - - if (rawMsg.TypeUrl.IsProtoType(MsgGrantAuthorization.MsgInstantiateContract)) - { - ((Secret.Compute.V1Beta1.MsgInstantiateContract)msg).InitMsg = ByteString.CopyFrom(new ArraySegment(plaintext, 64, plaintext.Length - 64).ToArray()); - } - else if (rawMsg.TypeUrl.IsProtoType(MsgGrantAuthorization.MsgExecuteContract)) - { - ((Secret.Compute.V1Beta1.MsgExecuteContract)msg).Msg = ByteString.CopyFrom(new ArraySegment(plaintext, 64, plaintext.Length - 64).ToArray()); - } - - nonces[i] = nonce; // Fill nonces array to later use it in output decryption - - } - catch (Exception ex) - { - // Not encrypted or can't decrypt because not original sender - } - } - } - - decodedTx.Body.Messages[i] = Any.Pack(msg); - } - - txResponse.Tx = Any.Pack(decodedTx); - - var secretTx = new SecretTx(txResponse); - - Func getNounce = (msgIndex) => - { - if (msgIndex < nonces.Count()) - { - var nonce = nonces[msgIndex]; - if (nonce != null && nonce.Length == 32) - { - return nonce; - } - } - return null; - }; - - var tx = txResponse; - List jsonLogs = null; - var arrayLog = new List(); - var rawLog = (string)tx.RawLog.Clone(); - - if (tx.Code == 0 && !string.IsNullOrEmpty(tx.RawLog)) - { - jsonLogs = JsonConvert.DeserializeObject>(tx.RawLog); - - for (int i = 0; i < jsonLogs.Count(); i++) - { - var log = jsonLogs[i]; - if (!log.MsgIndex.HasValue) - { - log.MsgIndex = i; - } - - foreach (var ev in log.Events) - { - foreach (var attr in ev.Attributes) - { - // Try to decrypt - if (tryToDecrypt && ev.MessageType.Equals("wasm", StringComparison.CurrentCultureIgnoreCase)) - { - var nonce = getNounce(log.MsgIndex.GetValueOrDefault()); - if (nonce != null) - { - try - { - if (attr.Key.IsBase64String()) - { - attr.Key = Encoding.UTF8.GetString(await Encryption.Decrypt(Convert.FromBase64String(attr.Key), nonce)); - } - } - catch { } - try - { - if (attr.Value.IsBase64String()) - { - attr.Value = Encoding.UTF8.GetString(await Encryption.Decrypt(Convert.FromBase64String(attr.Value), nonce)); - } - } - catch { } - } - } - - arrayLog.Add(new CosmosArrayLog() - { - MsgIndex = log.MsgIndex.GetValueOrDefault(), - EventType = ev.MessageType, - Key = attr.Key, - Value = attr.Value, - }); - } - } - } - } - else if (tx.Code != 0 && !string.IsNullOrEmpty(tx.RawLog)) - { - try - { - var errorMatches = _errorMessageRegEx.Match(tx.RawLog); - if (errorMatches.Success && - !string.IsNullOrEmpty(errorMatches.Groups["msgIndex"]?.Value) && - !string.IsNullOrEmpty(errorMatches.Groups["encrypted"]?.Value)) - { - var encryptedError = Convert.FromBase64String(errorMatches.Groups["encrypted"].Value); - var msgIndex = Convert.ToInt32(errorMatches.Groups["msgIndex"].Value); - var nonce = getNounce(msgIndex); - if (nonce != null) - { - var decryptedBase64Error = Encoding.UTF8.GetString(await Encryption.Decrypt(encryptedError, nonce)); - - rawLog = rawLog.Replace($"encrypted: {errorMatches.Groups["encrypted"].Value}", decryptedBase64Error); - } - } - - // Check for errors - // Cosmos SDK Errors - // https://github.com/cosmos/cosmos-sdk/blob/main/types/errors/errors.go - if (tx.Codespace.Equals("sdk", StringComparison.CurrentCultureIgnoreCase)) - { - var isEnumParsed = System.Enum.TryParse(tx.Code.ToString(), true, out CosmosSdkErrorEnum errorEnum); - if (isEnumParsed) - { - secretTx.Exceptions.Add(new CosmosSdkException(errorEnum, txResponse)); - //throw new CosmosSdkException(errorEnum, encryptedResult); - } - } - } - catch (Exception ex) - { - // Not encrypted or can't decrypt because not original sender - secretTx.Exceptions.Add(new Exception("Not encrypted or can't decrypt because not original sender")); - secretTx.Exceptions.Add(ex); - } - } - - var txMsgData = TxMsgData.Parser.ParseFrom(Convert.FromHexString(tx.Data)); - var data = new byte[txMsgData.Data.Count][]; - for (int msgIndex = 0; msgIndex < txMsgData.Data.Count; msgIndex++) - { - data[msgIndex] = txMsgData.Data[msgIndex].Data.ToByteArray(); - if (data[msgIndex]?.Length > 0) - { - var nonce = getNounce(msgIndex); - if (nonce != null && tryToDecrypt) - { - var rawMsg = decodedTx?.Body?.Messages[msgIndex]; - var messageDecoder = MsgDecoderRegistry.Get(rawMsg.TypeUrl); - if (messageDecoder == null) continue; - - var msg = messageDecoder(rawMsg); - - if (rawMsg.TypeUrl.IsProtoType(MsgGrantAuthorization.MsgInstantiateContract)) - { - var decoded = Secret.Compute.V1Beta1.MsgInstantiateContractResponse.Parser.ParseFrom(txMsgData.Data[msgIndex].Data); - var decrypted = Convert.FromBase64String(Encoding.UTF8.GetString(await Encryption.Decrypt(decoded.Data.ToByteArray(), nonce))); - var decryptedMsg = new Secret.Compute.V1Beta1.MsgInstantiateContractResponse() - { - Address = decoded.Address, - Data = ByteString.CopyFrom(decrypted) - }; - data[msgIndex] = decryptedMsg.Encode(); - } - else if (rawMsg.TypeUrl.IsProtoType(MsgGrantAuthorization.MsgExecuteContract)) - { - var decoded = Secret.Compute.V1Beta1.MsgExecuteContractResponse.Parser.ParseFrom(txMsgData.Data[msgIndex].Data); - var decrypted = Convert.FromBase64String(Encoding.UTF8.GetString(await Encryption.Decrypt(decoded.Data.ToByteArray(), nonce))); - var jsonData = Encoding.UTF8.GetString(decrypted); - var decryptedMsg = new Secret.Compute.V1Beta1.MsgExecuteContractResponse() - { - Data = ByteString.CopyFrom(decrypted) - }; - data[msgIndex] = decryptedMsg.Encode(); - } - } - else - { - data[msgIndex] = txMsgData.Data[msgIndex].Data.ToByteArray(); - } - } - } - - secretTx.RawLog = rawLog; - secretTx.JsonLog = jsonLogs; - secretTx.ArrayLog = arrayLog; - secretTx.Data = data; - secretTx.Success = true; - - result.Add(secretTx); - - }); - - if (!result.IsEmpty) - { - return result.ToArray(); - } - - return null; - } - } diff --git a/src/Tx/Vesting.Msg.cs b/src/Tx/Vesting.Msg.cs index 052f5569..a0a65d6c 100644 --- a/src/Tx/Vesting.Msg.cs +++ b/src/Tx/Vesting.Msg.cs @@ -28,7 +28,7 @@ public MsgCreateVestingAccount(string fromAddress, string toAddress, Coin[] amou Delayed = delayed; } - public override async Task ToProto(SecretEncryptionUtils utils) + public override async Task ToProto() { var msgUnjail = new Cosmos.Vesting.V1Beta1.MsgCreateVestingAccount() { @@ -45,7 +45,7 @@ public override async Task ToProto(SecretEncryptionUtils utils) return msgUnjail; } - public override async Task ToAmino(SecretEncryptionUtils utils) + public override async Task ToAmino() { throw new NotImplementedException("MsgCreateVestingAccount ToAmino is not implemented."); } diff --git a/src/Tx/VestingTx.Simulate.cs b/src/Tx/VestingTx.Simulate.cs deleted file mode 100644 index 6e747ef7..00000000 --- a/src/Tx/VestingTx.Simulate.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace SecretNET.Tx; - -public class VestingTxSimulate -{ - private TxClient _tx; - - internal VestingTxSimulate(TxClient tx) - { - _tx = tx; - } - - /// - /// Simulates "MsgCreateVestingAccount defines a message that enables creating a vesting account". - /// - /// - /// - /// - public async Task CreateVestingAccount(MsgCreateVestingAccount msg, TxOptions? txOptions = null) - { - return await _tx.Simulate(msg, txOptions); - } - -} - \ No newline at end of file diff --git a/src/Tx/VestingTx.cs b/src/Tx/VestingTx.cs index e4552a18..45e56449 100644 --- a/src/Tx/VestingTx.cs +++ b/src/Tx/VestingTx.cs @@ -3,17 +3,10 @@ public class VestingTx { private TxClient _tx; - private VestingTxSimulate _txSimulte; internal VestingTx(TxClient tx) { _tx = tx; - _txSimulte = new VestingTxSimulate(tx); - } - - public VestingTxSimulate Simulate - { - get { return _txSimulte; } } /// @@ -22,9 +15,10 @@ public VestingTxSimulate Simulate /// /// /// - public async Task CreateVestingAccount(MsgCreateVestingAccount msg, TxOptions? txOptions = null) + public async Task> CreateVestingAccount(Cosmos.Vesting.V1Beta1.MsgCreateVestingAccount msg, TxOptions? txOptions = null) { - return await _tx.Broadcast(msg, txOptions); + var txResult = await _tx.Broadcast(msg, txOptions); + return new SingleSecretTx(txResult); } } From d881f9aa03bb2c95e5dc9494a7fecb7ac4429546 Mon Sep 17 00:00:00 2001 From: 0xxCodemonkey Date: Fri, 23 Sep 2022 10:28:33 +0200 Subject: [PATCH 22/31] Add examples, remove unnecessary messages, clean up --- examples/SecretNET.Examples/Program.cs | 256 +++++++++++++++ .../SecretNET.Examples.csproj | 14 + .../ibc/applications/transfer/v1/tx.proto | 12 +- src/ISecretNetworkClient.cs | 6 +- src/Query/Queries.cs | 82 ++++- src/SecretNET.csproj | 2 +- src/SecretNET.sln | 6 + src/SecretNetworkClient.cs | 3 +- src/Tx/Authz.Msg.cs | 281 ---------------- src/Tx/AuthzTx.cs | 6 +- src/Tx/Bank.Msg.cs | 161 --------- src/Tx/BankTx.cs | 13 +- src/Tx/Crisis.Msg.cs | 49 --- src/Tx/CrisisTx.cs | 2 +- src/Tx/Distribution.Msg.cs | 182 ---------- src/Tx/DistributionTx.cs | 8 +- src/Tx/Evidence.Msg.cs | 31 -- src/Tx/EvidenceTx.cs | 2 +- src/Tx/Feegrant.Msg.cs | 110 ------- src/Tx/FeegrantTx.cs | 4 +- src/Tx/Gov.Msg.cs | 310 ------------------ src/Tx/GovTx.cs | 8 +- src/Tx/IBC_Channel.Msg.cs | 171 ---------- src/Tx/IBC_ChannelTx.cs | 20 +- src/Tx/IBC_Client.Msg.cs | 54 --- src/Tx/IBC_ClientTx.cs | 8 +- src/Tx/IBC_Connection.Msg.cs | 38 --- src/Tx/IBC_ConnectionTx.cs | 8 +- src/Tx/IBC_Transfer.Msg.cs | 99 ------ src/Tx/IBC_TransferTx.cs | 2 +- src/Tx/Registration.Msg.cs | 49 --- src/Tx/Slashing.Msg.cs | 44 --- src/Tx/SlashingTx.cs | 2 +- src/Tx/Staking.Msg.cs | 276 ---------------- src/Tx/StakingTx.cs | 10 +- src/Tx/TxClient.cs | 87 ++++- src/Tx/Vesting.Msg.cs | 57 ---- src/Tx/VestingTx.cs | 2 +- 38 files changed, 504 insertions(+), 1971 deletions(-) create mode 100644 examples/SecretNET.Examples/Program.cs create mode 100644 examples/SecretNET.Examples/SecretNET.Examples.csproj delete mode 100644 src/Tx/Authz.Msg.cs delete mode 100644 src/Tx/Bank.Msg.cs delete mode 100644 src/Tx/Crisis.Msg.cs delete mode 100644 src/Tx/Distribution.Msg.cs delete mode 100644 src/Tx/Evidence.Msg.cs delete mode 100644 src/Tx/Feegrant.Msg.cs delete mode 100644 src/Tx/Gov.Msg.cs delete mode 100644 src/Tx/IBC_Channel.Msg.cs delete mode 100644 src/Tx/IBC_Client.Msg.cs delete mode 100644 src/Tx/IBC_Connection.Msg.cs delete mode 100644 src/Tx/IBC_Transfer.Msg.cs delete mode 100644 src/Tx/Registration.Msg.cs delete mode 100644 src/Tx/Slashing.Msg.cs delete mode 100644 src/Tx/Staking.Msg.cs delete mode 100644 src/Tx/Vesting.Msg.cs diff --git a/examples/SecretNET.Examples/Program.cs b/examples/SecretNET.Examples/Program.cs new file mode 100644 index 00000000..ad606077 --- /dev/null +++ b/examples/SecretNET.Examples/Program.cs @@ -0,0 +1,256 @@ +global using Newtonsoft.Json; + +// SecretNET +global using SecretNET; +global using SecretNET.Tx; +global using SecretNET.Common; +global using SecretNET.Common.Storage; + + +// See https://aka.ms/new-console-template for more information + +#region *** Helper functions / Objects *** + +Action writeHeadline = (text) => +{ + Console.ForegroundColor = ConsoleColor.Cyan; + Console.WriteLine($"\r\n**************** {text} ****************\r\n"); + Console.ForegroundColor = ConsoleColor.White; +}; + +Action logSecretTx = (name, tx) => +{ + Console.WriteLine($"{name} Txhash: {tx?.Txhash}"); + Console.ForegroundColor = ConsoleColor.Gray; + Console.WriteLine($"(Gas used: {tx.GasUsed} - Gas wanted {tx.GasWanted})"); + Console.ForegroundColor = ConsoleColor.White; + + //Console.WriteLine($"\r\nCodeId: {JsonConvert.SerializeObject(tx.GetResponseJson(), Formatting.Indented)}"); + + if (tx is SingleSecretTx) + { + Console.WriteLine($"\r\nCodeId: {((SingleSecretTx)tx).Response.CodeId}"); + } + + if (tx is SingleSecretTx) + { + Console.WriteLine($"\r\nContractAddress: {((SingleSecretTx)tx)?.Response?.Address}"); + } + + if (tx != null && (tx.Code > 0 || (tx.Exceptions?.Any()).GetValueOrDefault())) + { + Console.ForegroundColor = ConsoleColor.Red; + if (tx.Code > 0 && !string.IsNullOrEmpty(tx.Codespace)) + { + Console.WriteLine($"\r\n!!!!!!!!!!!! Something went wrong => Code: {tx.Code}; Codespace: {tx.Codespace} !!!!!!!!!!!!"); + } + if ((tx.Exceptions?.Any()).GetValueOrDefault()) + { + foreach (var ex in tx.Exceptions) + { + Console.WriteLine($"\r\n!!!!!!!!!!!! Exception: {ex.Message} !!!!!!!!!!!!"); + } + } + Console.WriteLine($"\r\n{tx.RawLog}"); + Console.ForegroundColor = ConsoleColor.White; + Console.ReadLine(); + } +}; + +// TxOptions +var txOptions = new TxOptions() +{ + GasLimit = 60_000, + GasPriceInFeeDenom = 0.26F +}; + +var txOptionsExecute = new TxOptions() +{ + GasLimit = 300_000, + GasPriceInFeeDenom = 0.26F +}; + +var txOptionsUpload = new TxOptions() +{ + GasLimit = 12_000_000, + GasPriceInFeeDenom = 0.26F +}; + +#endregion + +writeHeadline("Setup SecretNetworkClient / Wallet"); + +//var storageProvider = new InMemoryOnlyStorage(); // Temporary and most likely only for DEV +var storageProvider = new AesEncryptedFileStorage("","SuperSecurePassword"); +var createWalletOptions = new CreateWalletOptions(storageProvider); + +Wallet wallet = null; +if (await storageProvider.HasPrivateKey()) +{ + var storedMnemonic = await storageProvider.GetFirstMnemonic(); + wallet = await Wallet.Create(storedMnemonic, options: createWalletOptions); +} +else +{ + wallet = await Wallet.Create(options: createWalletOptions); + + Console.WriteLine("Please first fund the wallet with some SCRT via https://faucet.pulsar.scrttestnet.com/ "); + Console.ReadLine(); +} + +var gprcUrl = "https://grpc.testnet.secretsaturn.net"; // get from https://github.com/scrtlabs/api-registry +var chainId = "pulsar-2"; + +var createClientOptions = new CreateClientOptions(gprcUrl, chainId, wallet); +var secretClient = new SecretNetworkClient(createClientOptions); + +Console.WriteLine("wallet.Address: " + wallet.Address); + + +#region *** Get Balance *** + +// *** Get Balance (1000000 uscrt == 1 SCRT) *** + +writeHeadline("Get Balance"); + +var response = await secretClient.Query.Bank.Balance(wallet.Address); +Console.WriteLine($"Balance: {(float.Parse(response.Amount) / 1000000f)} SCRT"); + +//Console.ReadLine(); + +#endregion + +#region *** Get Subacccount and Send $SCRT *** + +// Send SCRT +var subaccountWallet = await wallet.GetSubaccount(1); +Console.WriteLine($"\r\nSubaccount.Address: {subaccountWallet.Address}"); + +var sendResponse = await secretClient.Tx.Bank.Send(subaccountWallet.Address, 1000000); +Console.WriteLine($"BroadcastResponse: {(sendResponse.Code == 0 ? "Success" : "Error (see log)")}"); + +var r1 = await secretClient.Query.Bank.Balance(subaccountWallet.Address); +Console.WriteLine($"Subaccount Balance: {(float.Parse(r1.Amount) / 1000000f)} SCRT\r\n"); + +//Console.ReadLine(); + +#endregion + +#region *** Auth *** + +// *** Get Account *** + +writeHeadline("Get Account"); + +var accountResponse = await secretClient.Query.Auth.Account(wallet.Address); +Console.WriteLine("AccountResponse:\r\n" + JsonConvert.SerializeObject(accountResponse, Formatting.Indented) + "\r\n"); + +//Console.ReadLine(); + +#endregion + +#region *** Codes *** + +// *** Get Codes with source *** + +writeHeadline("Get my Codes with source"); + +var codesResponse = await secretClient.Query.Compute.Codes(); +var withSource = codesResponse.Where(c => !String.IsNullOrEmpty(c.Source) && c.CreatorAddress == wallet.Address).ToList(); +Console.WriteLine($"My Codes with source (Count: {withSource.Count} ):\r\n" + JsonConvert.SerializeObject(withSource, Formatting.Indented) + "\r\n"); + +//Console.ReadLine(); + +#endregion + +#region *** Smart Contract (Upload, Init, Query, Execute) *** + +// *** Smart Contract (Upload, Init, Query, Execute) *** + +writeHeadline("Upload Contract (mysimplecounter.wasm.gz)"); + +// *** Upload Contract *** +// https://secretjs.scrt.network/#secretjstxcomputestorecode + +byte[] wasmByteCode = File.ReadAllBytes(@"C:\_GitHub\mhorn69\secretNET\Resources\SmartContracts\mysimplecounter.wasm.gz"); + +// MsgStoreCode +var msgStoreCodeCounter = new MsgStoreCode(wasmByteCode, + source: "https://github.com/scrtlabs/secret-template", // Source is a valid absolute HTTPS URI to the contract's source code, optional + builder: "enigmampc/secret-contract-optimizer:latest" // Builder is a valid docker image name with tag, optional + ); + +var storeCodeResponse = await secretClient.Tx.Compute.StoreCode(msgStoreCodeCounter, txOptions: txOptionsUpload); +logSecretTx("StoreCodeResponse", storeCodeResponse); + +// *** Init Contract *** +writeHeadline("Init Contract with CodeId " + storeCodeResponse.Response.CodeId); + +string? contractAddress = null; +string contractCodeHash = null; +if (storeCodeResponse.Response.CodeId > 0) +{ + var codeId = storeCodeResponse.Response.CodeId; + var initMsg = new { count = 100 }; + Console.WriteLine("InstantiateContract:\r\n" + JsonConvert.SerializeObject(initMsg, Formatting.Indented) + "\r\n"); + + var msgInstantiateCounterContract = new SecretNET.Tx.MsgInstantiateContract(codeId, $"MySimpleCouter {codeId}", initMsg); + + var instantiateCounterContractResponse = await secretClient.Tx.Compute.InstantiateContract(msgInstantiateCounterContract, txOptions: txOptionsUpload); + logSecretTx("InstantiateContract", instantiateCounterContractResponse); + + contractAddress = instantiateCounterContractResponse.Response.Address; + if (!string.IsNullOrEmpty(contractAddress)) + { + contractCodeHash = await secretClient.Query.Compute.GetCodeHash(contractAddress); + Console.WriteLine("ContractCodeHash: " + contractCodeHash); + } +} + +//Console.ReadLine(); + +#region *** Query Contract *** + +//string contractAddress = "Set manual if needed"; +//string contractCodeHash = "Set manual if needed"; + +// *** Query Contract *** + +writeHeadline("Query Contract with address " + contractAddress); + +var queryMsg = new { get_count = new { } }; + +Console.WriteLine("Query : " + JsonConvert.SerializeObject(queryMsg, Formatting.Indented) + "\r\n"); + +var queryContractResult = await secretClient.Query.Compute.QueryContract(contractAddress, queryMsg, contractCodeHash); +Console.WriteLine("QueryContractResult:\r\n " + queryContractResult.Response); + +//Console.ReadLine(); + +#endregion + +#region *** Execute Contract *** + +// *** Execute Contract *** + +writeHeadline("Execute Contract with address " + contractAddress); + +var executeMsg = new { increment = new { } }; + +Console.WriteLine("Execute : " + JsonConvert.SerializeObject(executeMsg, Formatting.Indented) + "\r\n"); + +var msgExecuteContract = new SecretNET.Tx.MsgExecuteContract(contractAddress, executeMsg, contractCodeHash); + +var executeContractResponse = await secretClient.Tx.Compute.ExecuteContract(msgExecuteContract, txOptionsExecute); +logSecretTx("ExecuteContract", executeContractResponse); + +Thread.Sleep(1000); // give some time to let it settle + +var queryContractResult2 = await secretClient.Query.Compute.QueryContract(contractAddress, queryMsg, contractCodeHash); +Console.WriteLine("\r\nQueryContractResult (again) :\r\n " + queryContractResult2.Response); + +Console.ReadLine(); + +#endregion + +#endregion \ No newline at end of file diff --git a/examples/SecretNET.Examples/SecretNET.Examples.csproj b/examples/SecretNET.Examples/SecretNET.Examples.csproj new file mode 100644 index 00000000..662fa686 --- /dev/null +++ b/examples/SecretNET.Examples/SecretNET.Examples.csproj @@ -0,0 +1,14 @@ + + + + Exe + net6.0 + enable + enable + + + + + + + diff --git a/src/Api/Protos/ibc/applications/transfer/v1/tx.proto b/src/Api/Protos/ibc/applications/transfer/v1/tx.proto index 8f1392b0..1a2e3ec8 100644 --- a/src/Api/Protos/ibc/applications/transfer/v1/tx.proto +++ b/src/Api/Protos/ibc/applications/transfer/v1/tx.proto @@ -32,7 +32,17 @@ message MsgTransfer { // the recipient address on the destination chain string receiver = 5; // Timeout height relative to the current block height. - // The timeout is disabled when set to 0. + // The timeout is disabled when undefined or set to 0. + // + // Height is a monotonically increasing data type + // that can be compared against another Height for the purposes of updating and + // freezing clients. + // + // Normally the RevisionHeight is incremented at each height while keeping + // RevisionNumber the same. However some consensus algorithms may choose to + // reset the height in certain conditions e.g. hard forks, state-machine + // breaking changes In these cases, the RevisionNumber is incremented so that + // height continues to be monitonically increasing even as the RevisionHeight gets reset ibc.core.client.v1.Height timeout_height = 6 [(gogoproto.moretags) = "yaml:\"timeout_height\"", (gogoproto.nullable) = false]; // Timeout timestamp in absolute nanoseconds since unix epoch. diff --git a/src/ISecretNetworkClient.cs b/src/ISecretNetworkClient.cs index ae44cff9..b6b0ef3e 100644 --- a/src/ISecretNetworkClient.cs +++ b/src/ISecretNetworkClient.cs @@ -45,7 +45,7 @@ public interface ISecretNetworkClient /// The messages. /// The tx options. /// Task<System.ValueTuple<System.Byte[], ComputeMsgToNonce>>. - public Task PrepareAndSign(Tx.MsgBase[] messages, TxOptions? txOptions = null); + public Task PrepareAndSign(Tx.MsgBase[] messages, TxOptions txOptions = null); /// /// Signs the specified messages. @@ -55,7 +55,7 @@ public interface ISecretNetworkClient /// The memo. /// The explicit signer data. /// Task<System.ValueTuple<TxRaw, ComputeMsgToNonce>>. - public Task Sign(Tx.MsgBase[] messages, StdFee fee, string? memo = null, SignerData? explicitSignerData = null); + public Task Sign(Tx.MsgBase[] messages, StdFee fee, string memo = null, SignerData explicitSignerData = null); /// /// Signs the transaction (direct). @@ -65,6 +65,6 @@ public interface ISecretNetworkClient /// The signer data. /// The memo. /// Task<System.ValueTuple<TxRaw, ComputeMsgToNonce>>. - public Task SignDirect(Tx.MsgBase[] messages, StdFee fee, SignerData signerData, string? memo = null); + public Task SignDirect(Tx.MsgBase[] messages, StdFee fee, SignerData signerData, string memo = null); } diff --git a/src/Query/Queries.cs b/src/Query/Queries.cs index d91491d0..82c781ad 100644 --- a/src/Query/Queries.cs +++ b/src/Query/Queries.cs @@ -5,6 +5,11 @@ namespace SecretNET.Query; +/// +/// Provides access to all query types / methods. +/// Implements the +/// +/// public class Queries : GprcBase { private Service.ServiceClient _txClient; @@ -123,94 +128,166 @@ public async Task GetTxsEvent(GetTxsEventRequest request, bool tryTo } /// - /// AuthQuerier is the query interface for the x/auth module + /// Account queries. /// public AuthQueryClient Auth { get { return _authQuery; } } + /// + /// Authorization queries (Grants, etc.). + /// + /// The authz. public AuthzQueryClient Authz { get { return _authzQuery; } } + /// + /// Bank queries (balance, etc.). + /// + /// The bank. public BankQueryClient Bank { get { return _bankQuery; } } + /// + /// Compute queries (QueryContract, GetCodeHash, ContractInfo, etc.). + /// + /// The compute. public ComputeQueryClient Compute { get { return _computeQuery; } } + /// + /// Distribution queries. + /// + /// The distribution. public DistributionQueryClient Distribution { get { return _distributionQuery; } } + /// + /// Evidence queries. + /// + /// The evidence. public EvidenceQueryClient Evidence { get { return _evidenceQuery; } } + /// + /// Feegrant queries. + /// + /// The feegrant. public FeegrantQueryClient Feegrant { get { return _feegrantQuery; } } + /// + /// Governance queries. + /// + /// The gov. public GovQueryClient Gov { get { return _govQuery; } - } + } + /// + /// IBC Channel queries. + /// + /// The ibc channel. public IbcChannelQueryClient IbcChannel { get { return _ibcChannelQuery; } } + /// + /// IBC Client queries. + /// + /// The ibc client. public IbcClientQueryClient IbcClient { get { return _ibcClientQuery; } } + /// + /// IBC Connection queries. + /// + /// The ibc connection. public IbcConnectionQueryClient IbcConnection { get { return _ibcConnectionQuery; } } + /// + /// IBC Transfer queries. + /// + /// The ibc transfer. public IbcTransferQueryClient IbcTransfer { get { return _ibcTransferQuery; } } + /// + /// Mint queries. + /// + /// The mint. public MintQueryClient Mint { get { return _mintQuery; } } + /// + /// Params queries. + /// + /// The parameters. public ParamsQueryClient Params { get { return _paramsQuery; } } + /// + /// Registration queries (TxKey, etc). + /// + /// The registration. public RegistrationQueryClient Registration { get { return _registrationQuery; } } + /// + /// Slashing queries. + /// + /// The slashing. public SlashingQueryClient Slashing { get { return _slashingQuery; } } + /// + /// Staking queries. + /// + /// The staking. public StakingQueryClient Staking { get { return _stakingQuery; } } + /// + /// Tendermint queries (GetNodeInfo, GetLatestBlock, GetBlockByHeight, etc). + /// + /// The tendermint. public TendermintQueryClient Tendermint { get { return _tendermintQuery; } } + /// + /// Upgrade queries. + /// + /// The upgrade. public UpgradeQueryClient Upgrade { get { return _upgradeQuery; } @@ -266,7 +343,6 @@ await Parallel.ForEachAsync(txResponses, async (txResponse, cancellationToken) = try { var nonce = new ArraySegment(inputMsgBytes, 0, 32).ToArray(); - //Console.WriteLine("DecodeTxResponses nonce: " + nonce.ToHexString()); var accountPubkey = new ArraySegment(inputMsgBytes, 32, 32).ToArray(); // unused in decryption var ciphertext = new ArraySegment(inputMsgBytes, 64, inputMsgBytes.Length - 64).ToArray(); diff --git a/src/SecretNET.csproj b/src/SecretNET.csproj index 6d671b46..e1a86b45 100644 --- a/src/SecretNET.csproj +++ b/src/SecretNET.csproj @@ -24,7 +24,7 @@ 0.3.0 NuGet_README.md https://github.com/0xxCodemonkey/SecretNET - Secret Network;Blockchain;Privacy;Cosmos;IBC;MAUI; + Secret Network;Blockchain;Privacy;Cosmos-SDK;IBC;MAUI;Crypto;Web3; SecretNetwork_Logo.png https://github.com/0xxCodemonkey/SecretNET diff --git a/src/SecretNET.sln b/src/SecretNET.sln index cd9ed319..9d9ab68f 100644 --- a/src/SecretNET.sln +++ b/src/SecretNET.sln @@ -7,6 +7,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SecretNET", "SecretNET.cspr EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SecretNET.Tests", "..\test\SecretNET.Tests.csproj", "{8E466679-01AF-4767-A2DA-4634FB51CF40}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SecretNET.Examples", "..\examples\SecretNET.Examples\SecretNET.Examples.csproj", "{B5D33958-B38D-48DE-847D-56307DCF6EEF}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -21,6 +23,10 @@ Global {8E466679-01AF-4767-A2DA-4634FB51CF40}.Debug|Any CPU.Build.0 = Debug|Any CPU {8E466679-01AF-4767-A2DA-4634FB51CF40}.Release|Any CPU.ActiveCfg = Release|Any CPU {8E466679-01AF-4767-A2DA-4634FB51CF40}.Release|Any CPU.Build.0 = Release|Any CPU + {B5D33958-B38D-48DE-847D-56307DCF6EEF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B5D33958-B38D-48DE-847D-56307DCF6EEF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B5D33958-B38D-48DE-847D-56307DCF6EEF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B5D33958-B38D-48DE-847D-56307DCF6EEF}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/SecretNetworkClient.cs b/src/SecretNetworkClient.cs index 815317ee..df2794ad 100644 --- a/src/SecretNetworkClient.cs +++ b/src/SecretNetworkClient.cs @@ -166,7 +166,7 @@ public AccessControl.PermitSigner Permit /// The messages. /// The tx options. /// System.ValueTuple<System.Byte[], ComputeMsgToNonce>. - public async Task PrepareAndSign(MsgBase[] messages, TxOptions? txOptions = null) + public async Task PrepareAndSign(MsgBase[] messages, TxOptions txOptions = null) { txOptions = txOptions ?? new TxOptions(); @@ -490,7 +490,6 @@ private byte[] ExtractNonce(IMessage protoMsg) { var msg = (Secret.Compute.V1Beta1.MsgInstantiateContract)protoMsg; var slice = new ArraySegment(msg.InitMsg.Span.ToArray(), 0, 32).ToArray(); - Console.WriteLine("ExtractNonce nonce: " + slice.ToHexString()); return slice; } return new byte[0]; diff --git a/src/Tx/Authz.Msg.cs b/src/Tx/Authz.Msg.cs deleted file mode 100644 index f314df09..00000000 --- a/src/Tx/Authz.Msg.cs +++ /dev/null @@ -1,281 +0,0 @@ -using Cosmos.Authz.V1Beta1; -using System.Net; - -namespace SecretNET.Tx; - -public class SendAuthorization : MsgBase -{ - public override string MsgType { get; } = MsgTypeNames.SendAuthorization; - - public Coin[] Coins { get; set; } - - public override async Task ToProto() - { - var sendAuthorization = new Cosmos.Bank.V1Beta1.SendAuthorization(); - sendAuthorization.SpendLimit.Add(Coins.Select(c => new Cosmos.Base.V1Beta1.Coin() { Amount = c.Amount, Denom = c.Denom }).ToArray()); - - return sendAuthorization; - } - - public override async Task ToAmino() - { - throw new Exception("SendAuthorization ToAmino is not implemented."); - } -} - -/// -/// AuthorizationType defines the type of staking module authorization type -/// -public enum StakeAuthorizationType : byte -{ - Unspecified = 0, - /// - /// defines an authorization type for Msg/Delegate - /// - Delegate = 1, - /// - /// defines an authorization type for Msg/Undelegate - /// - Undelegate = 2, - /// - /// defines an authorization type for Msg/BeginRedelegate - /// - Redelegate = 3, -} - -public class StakeAuthorization : MsgBase -{ - public override string MsgType { get; } = MsgTypeNames.StakeAuthorization; - - /// - /// max_tokens specifies the maximum amount of tokens can be delegate to a validator. - /// If it is empty, there is no spend limit and any amount of coins can be delegated. - /// - public Coin MaxTokens { get; set; } - - /// - /// allow_list specifies list of validator addresses to whom grantee can delegate tokens on behalf of granter's account. - /// - public string[] AllowList { get; set; } - - /// - /// deny_list specifies list of validator addresses to whom grantee can not delegate tokens. - /// - public string[] DenyList { get; set; } - - /// - /// deny_list specifies list of validator addresses to whom grantee can not delegate tokens. - /// - public StakeAuthorizationType AuthorizationType { get; set; } - - public override async Task ToProto() - { - var sendAuthorization = new Cosmos.Staking.V1Beta1.StakeAuthorization(); - if (MaxTokens != null) - { - sendAuthorization.MaxTokens = new Cosmos.Base.V1Beta1.Coin() - { - Denom = MaxTokens.Denom, - Amount = MaxTokens.Amount, - }; - } - if (AllowList != null && AllowList.Length > 0) - { - foreach (var i in AllowList) - { - sendAuthorization.AllowList = new Cosmos.Staking.V1Beta1.StakeAuthorization.Types.Validators(); - sendAuthorization.AllowList.Address.Add(i.ToString()); - } - } - if (DenyList != null && DenyList.Length > 0) - { - foreach (var i in DenyList) - { - sendAuthorization.DenyList = new Cosmos.Staking.V1Beta1.StakeAuthorization.Types.Validators(); - sendAuthorization.DenyList.Address.Add(i.ToString()); - } - } - sendAuthorization.AuthorizationType = (Cosmos.Staking.V1Beta1.AuthorizationType)(byte)AuthorizationType; - - return sendAuthorization; - } - - public override async Task ToAmino() - { - throw new Exception("SendAuthorization ToAmino is not implemented."); - } -} - -/// -/// MsgGrant is a request type for Grant method. It declares authorization to the grantee -/// on behalf of the granter with the provided expiration time. -/// -public class MsgGrant : MsgBase -{ - public override string MsgType { get; } = MsgGrantAuthorization.MsgGrant; - - public string Granter { get; set; } - public string Grantee { get; set; } - - public MsgBase Authorization { get; set; } - - /// - /// Represents seconds of UTC time since Unix epoch 1970-01-01T00:00:00Z. - /// => use DateTime.UnixEpoch - /// - public ulong Expiration { get; set; } - - public override async Task ToProto() - { - Cosmos.Authz.V1Beta1.MsgGrant msgGrant = null; - - if (Authorization != null) - { - // SendAuthorization or StakeAuthorization - if (Authorization.MsgType.IsProtoType(MsgTypeNames.SendAuthorization) || - Authorization.MsgType.IsProtoType(MsgTypeNames.StakeAuthorization)) - { - msgGrant = new Cosmos.Authz.V1Beta1.MsgGrant - { - Grant = new Cosmos.Authz.V1Beta1.Grant() - { - Authorization = Any.Pack((await Authorization.ToProto()),""), - Expiration = new Timestamp() - { - Seconds = (long)Expiration - } - } - }; - } - // GenericAuthorization - if (MsgGrantAuthorization.GrantMessageTypes.Contains(Authorization.MsgType)) - { - var genericAuthorization = new Cosmos.Authz.V1Beta1.GenericAuthorization() - { - Msg = Authorization.MsgType - }; - msgGrant = new Cosmos.Authz.V1Beta1.MsgGrant - { - Grant = new Cosmos.Authz.V1Beta1.Grant() - { - Authorization = Any.Pack(genericAuthorization,""), - Expiration = new Timestamp() - { - Seconds = (long)Expiration - } - } - }; - } - - if (msgGrant != null) - { - msgGrant.Granter = Granter; - msgGrant.Grantee = Grantee; - - return msgGrant; - } - } - return null; - } - - public override async Task ToAmino() - { - throw new Exception("MsgGrant ToAmino is not implemented."); - } -} - -/// -/// MsgExec attempts to execute the provided messages using authorizations granted to the grantee. -/// Each message should have only one signer corresponding to the granter of the authorization. -/// -public class MsgExec : MsgBase -{ - public override string MsgType { get; } = MsgGrantAuthorization.MsgExec; - - public string Grantee { get; set; } - - /// - /// Authorization Msg requests to execute. Each msg must implement Authorization interface - /// The x/authz will try to find a grant matching (msg.signers[0], grantee, MsgTypeURL(msg)) - /// triple and validate it. - /// - public MsgBase[] AuthorizationMsgs { get; set; } - - public override async Task ToProto() - { - if (AuthorizationMsgs != null && AuthorizationMsgs.Length > 0) - { - var msgExec = new Cosmos.Authz.V1Beta1.MsgExec() - { - Grantee = Grantee - }; - - var protoMsgs = new List(); - foreach (var m in AuthorizationMsgs) - { - var protoMsg = await m.ToProto(); - protoMsgs.Add(Any.Pack(protoMsg, "")); - } - - msgExec.Msgs.Add(protoMsgs.ToArray()); - - return msgExec; - } - return null; - } - - public override Task ToAmino() - { - throw new Exception("MsgExec ToAmino is not implemented."); - } -} - -/// -/// MsgRevoke revokes any authorization with the provided sdk.Msg type on the -/// granter's account with that has been granted to the grantee. -/// -public class MsgRevoke : MsgBase -{ - public override string MsgType { get; } = MsgGrantAuthorization.MsgRevoke; - - public string Granter { get; set; } - public string Grantee { get; set; } - - /// - /// revokes any authorization with the provided sdk.Msg type on the - /// granter's account with that has been granted to the grantee. - /// - public MsgBase RevokeAuthorization { get; set; } - - /// - /// Authorization Msg requests to execute. Each msg must implement Authorization interface - /// The x/authz will try to find a grant matching (msg.signers[0], grantee, MsgTypeURL(msg)) - /// triple and validate it. - /// - public MsgBase[] AuthorizationMsgs { get; set; } - - public override async Task ToProto() - { - if (RevokeAuthorization != null) - { - if (MsgGrantAuthorization.GrantMessageTypes.Contains(RevokeAuthorization.MsgType)) - { - - var msgRevoke = new Cosmos.Authz.V1Beta1.MsgRevoke - { - Grantee = Grantee, - Granter = Granter, - MsgTypeUrl = RevokeAuthorization.MsgType - }; - - return msgRevoke; - } - } - return null; - } - - public override Task ToAmino() - { - throw new NotImplementedException("MsgRevoke ToAmino is not implemented."); - } -} \ No newline at end of file diff --git a/src/Tx/AuthzTx.cs b/src/Tx/AuthzTx.cs index feb8fb48..27222201 100644 --- a/src/Tx/AuthzTx.cs +++ b/src/Tx/AuthzTx.cs @@ -17,7 +17,7 @@ internal AuthzTx(TxClient tx) /// /// /// - public async Task> Exec(Cosmos.Authz.V1Beta1.MsgExec msg, TxOptions? txOptions = null) + public async Task> Exec(Cosmos.Authz.V1Beta1.MsgExec msg, TxOptions txOptions = null) { var txResult = await _tx.Broadcast(msg, txOptions); return new SingleSecretTx(txResult); @@ -30,7 +30,7 @@ internal AuthzTx(TxClient tx) /// /// /// - public async Task> Grant(Cosmos.Authz.V1Beta1.MsgGrant msg, TxOptions? txOptions = null) + public async Task> Grant(Cosmos.Authz.V1Beta1.MsgGrant msg, TxOptions txOptions = null) { var txResult = await _tx.Broadcast(msg, txOptions); return new SingleSecretTx(txResult); @@ -43,7 +43,7 @@ internal AuthzTx(TxClient tx) /// /// /// - public async Task> Revoke(Cosmos.Authz.V1Beta1.MsgRevoke msg, TxOptions? txOptions = null) + public async Task> Revoke(Cosmos.Authz.V1Beta1.MsgRevoke msg, TxOptions txOptions = null) { var txResult = await _tx.Broadcast(msg, txOptions); return new SingleSecretTx(txResult); diff --git a/src/Tx/Bank.Msg.cs b/src/Tx/Bank.Msg.cs deleted file mode 100644 index 1caf84a6..00000000 --- a/src/Tx/Bank.Msg.cs +++ /dev/null @@ -1,161 +0,0 @@ -namespace SecretNET.Tx; - -/// -/// MsgSend represents a message to send coins from one account to another. -/// -public class MsgSend : MsgBase -{ - public override string MsgType { get; } = MsgGrantAuthorization.MsgSend; - - public string FromAddress { get; set; } - - public string ToAddress { get; set; } - - public Coin[] Amount { get; set; } - - - public MsgSend() { } - - public MsgSend(string fromAddress, string toAddress, Coin[] amount) - { - FromAddress = fromAddress; - ToAddress = toAddress; - Amount = amount; - } - - public override async Task ToProto() - { - var msgSend = new Cosmos.Bank.V1Beta1.MsgSend() - { - FromAddress = FromAddress, - ToAddress = ToAddress, - }; - msgSend.Amount.Add(Amount.Select(c => new Cosmos.Base.V1Beta1.Coin() { Amount = c.Amount, Denom = c.Denom }).ToArray()); - - return msgSend; - } - - public override async Task ToAmino() - { - var aminoMsg = new AminoMsg("cosmos-sdk/MsgSend"); - // order of properties must be sorted for amino signing!! - aminoMsg.Value = new - { - amount = Amount, - from_address = FromAddress, - to_address = ToAddress, - }; - - return aminoMsg; - } -} - -/// -/// Input / Output models transaction input for MsgMultiSend. -/// -public class Input -{ - [JsonProperty("address")] - public string Address { get; set; } - - [JsonProperty("coins")] - public Coin[] Coins { get; set; } - - public Input(string address, Coin[] coins) - { - Address = address; - Coins = coins; - } -} - -/// -/// Output models transaction outputs for MsgMultiSend. -/// -public class Output -{ - [JsonProperty("address")] - public string Address { get; set; } - - [JsonProperty("coins")] - public Coin[] Coins { get; set; } - - public Output(string address, Coin[] coins) - { - Address = address; - Coins = coins; - } -} - - -/// -/// MsgMultiSend represents an arbitrary multi-in, multi-out send message. */ -/// -public class MsgMultiSend : MsgBase -{ - public override string MsgType { get; } = MsgGrantAuthorization.MsgMultiSend; - - public Input[] Inputs { get; set; } - - public Output[] Outputs { get; set; } - - - public MsgMultiSend() { } - - public MsgMultiSend(Input[] inputs, Output[] outputs) - { - Inputs = inputs; - Outputs = outputs; - } - - public override async Task ToProto() - { - if (Inputs != null || Outputs != null) - { - var msgMultiSend = new Cosmos.Bank.V1Beta1.MsgMultiSend(); - - if (Inputs != null) - { - var inputList = new List(); - foreach (var i in Inputs) - { - var input = new Cosmos.Bank.V1Beta1.Input() - { - Address = i.Address - }; - input.Coins.Add(i.Coins.Select(c => new Cosmos.Base.V1Beta1.Coin() { Amount = c.Amount, Denom = c.Denom }).ToArray()); - } - msgMultiSend.Inputs.Add(inputList.ToArray()); - } - - if (Outputs != null) - { - var outputList = new List(); - foreach (var o in Outputs) - { - var input = new Cosmos.Bank.V1Beta1.Input() - { - Address = o.Address - }; - input.Coins.Add(o.Coins.Select(c => new Cosmos.Base.V1Beta1.Coin() { Amount = c.Amount, Denom = c.Denom }).ToArray()); - } - msgMultiSend.Outputs.Add(outputList.ToArray()); - } - - return msgMultiSend; - } - return null; - } - - public override async Task ToAmino() - { - var aminoMsg = new AminoMsg("cosmos-sdk/MsgMultiSend"); - // order of properties must be sorted for amino signing!! - aminoMsg.Value = new - { - inputs = Inputs, - outputs = Outputs - }; - - return aminoMsg; - } -} \ No newline at end of file diff --git a/src/Tx/BankTx.cs b/src/Tx/BankTx.cs index 0faf7900..6e27e546 100644 --- a/src/Tx/BankTx.cs +++ b/src/Tx/BankTx.cs @@ -17,27 +17,28 @@ internal BankTx(TxClient tx) /// /// /// - public async Task> Send(Cosmos.Bank.V1Beta1.MsgSend msg, TxOptions? txOptions = null) + public async Task> Send(Cosmos.Bank.V1Beta1.MsgSend msg, TxOptions txOptions = null) { var txResult = await _tx.Broadcast(msg, txOptions); return new SingleSecretTx(txResult); } /// - /// Sends the specified to address. + /// Sends SCRT to the specified to address. /// /// To address. - /// The amount. + /// The amount (1000000 uscrt == 1SCRT). + /// The denom, default 'uscrt' /// The tx options. /// SingleSecretTx<Cosmos.Bank.V1Beta1.MsgSendResponse>. - public async Task> Send(string toAddress, Coin[] amount, TxOptions? txOptions = null) + public async Task> Send(string toAddress, int amount, string denom = "uscrt", TxOptions txOptions = null) { var msgSend = new Cosmos.Bank.V1Beta1.MsgSend() { FromAddress = _tx.WalletAddress, ToAddress = toAddress, }; - msgSend.Amount.Add(amount.Select(c => new Cosmos.Base.V1Beta1.Coin() { Amount = c.Amount, Denom = c.Denom }).ToArray()); + msgSend.Amount.Add(new Cosmos.Base.V1Beta1.Coin() { Amount = amount.ToString(), Denom = denom }); var txResult = await _tx.Broadcast(msgSend, txOptions); return new SingleSecretTx(txResult); @@ -49,7 +50,7 @@ internal BankTx(TxClient tx) /// /// /// - public async Task> MultiSend(Cosmos.Bank.V1Beta1.MsgMultiSend msg, TxOptions? txOptions = null) + public async Task> MultiSend(Cosmos.Bank.V1Beta1.MsgMultiSend msg, TxOptions txOptions = null) { var txResult = await _tx.Broadcast(msg, txOptions); return new SingleSecretTx(txResult); diff --git a/src/Tx/Crisis.Msg.cs b/src/Tx/Crisis.Msg.cs deleted file mode 100644 index ffb60d9d..00000000 --- a/src/Tx/Crisis.Msg.cs +++ /dev/null @@ -1,49 +0,0 @@ -namespace SecretNET.Tx; - -public class MsgVerifyInvariant : MsgBase -{ - public override string MsgType { get; } = MsgGrantAuthorization.MsgVerifyInvariant; - - public string Sender { get; set; } - - public string InvariantModuleName { get; set; } - - public string InvariantRoute { get; set; } - - - public MsgVerifyInvariant() { } - - public MsgVerifyInvariant(string sender, string invariantModuleName, string invariantRoute) - { - Sender = sender; - InvariantModuleName = invariantModuleName; - InvariantRoute = invariantRoute; - } - - - public override async Task ToProto() - { - var msgVerifyInvariant = new Cosmos.Crisis.V1Beta1.MsgVerifyInvariant() - { - Sender = Sender, - InvariantModuleName = InvariantModuleName, - InvariantRoute = InvariantRoute, - }; - - return msgVerifyInvariant; - } - - public override async Task ToAmino() - { - var aminoMsg = new AminoMsg("cosmos-sdk/MsgVerifyInvariant"); - // order of properties must be sorted for amino signing!! - aminoMsg.Value = new - { - invariant_module_name = InvariantModuleName, - invariant_route = InvariantRoute, - sender = Sender - }; - - return aminoMsg; - } -} diff --git a/src/Tx/CrisisTx.cs b/src/Tx/CrisisTx.cs index 5e9bf290..347fe3fd 100644 --- a/src/Tx/CrisisTx.cs +++ b/src/Tx/CrisisTx.cs @@ -15,7 +15,7 @@ internal CrisisTx(TxClient tx) /// /// /// - public async Task> VerifyInvariant(Cosmos.Crisis.V1Beta1.MsgVerifyInvariant msg, TxOptions? txOptions = null) + public async Task> VerifyInvariant(Cosmos.Crisis.V1Beta1.MsgVerifyInvariant msg, TxOptions txOptions = null) { var txResult = await _tx.Broadcast(msg, txOptions); return new SingleSecretTx(txResult); diff --git a/src/Tx/Distribution.Msg.cs b/src/Tx/Distribution.Msg.cs deleted file mode 100644 index 7ce367e8..00000000 --- a/src/Tx/Distribution.Msg.cs +++ /dev/null @@ -1,182 +0,0 @@ -namespace SecretNET.Tx; - -/// -/// MsgSetWithdrawAddress sets the withdraw address for a delegator (or validator self-delegation). -/// -public class MsgSetWithdrawAddress : MsgBase -{ - public override string MsgType { get; } = MsgGrantAuthorization.MsgSetWithdrawAddress; - - public string DelegatorAddress { get; set; } - - public string WithdrawAddress { get; set; } - - - public MsgSetWithdrawAddress() { } - - public MsgSetWithdrawAddress(string delegatorAddress, string withdrawAddress) - { - DelegatorAddress = delegatorAddress; - WithdrawAddress = withdrawAddress; - } - - - public override async Task ToProto() - { - var msgSetWithdrawAddress = new Cosmos.Distribution.V1Beta1.MsgSetWithdrawAddress() - { - DelegatorAddress = DelegatorAddress, - WithdrawAddress = WithdrawAddress, - }; - - return msgSetWithdrawAddress; - } - - public override async Task ToAmino() - { - var aminoMsg = new AminoMsg("cosmos-sdk/MsgModifyWithdrawAddress"); - // order of properties must be sorted for amino signing!! - aminoMsg.Value = new - { - delegator_address = DelegatorAddress, - withdraw_address = WithdrawAddress, - }; - - return aminoMsg; - } -} - -/// -/// MsgWithdrawDelegatorReward represents delegation withdrawal to a delegator from a single validator. -/// -public class MsgWithdrawDelegatorReward : MsgBase -{ - public override string MsgType { get; } = MsgGrantAuthorization.MsgWithdrawDelegatorReward; - - public string DelegatorAddress { get; set; } - - public string ValidatorAddress { get; set; } - - - public MsgWithdrawDelegatorReward() { } - - public MsgWithdrawDelegatorReward(string delegatorAddress, string validatorAddress) - { - DelegatorAddress = delegatorAddress; - ValidatorAddress = validatorAddress; - } - - - public override async Task ToProto() - { - var msgWithdrawDelegatorReward = new Cosmos.Distribution.V1Beta1.MsgWithdrawDelegatorReward() - { - DelegatorAddress = DelegatorAddress, - ValidatorAddress = ValidatorAddress, - }; - - return msgWithdrawDelegatorReward; - } - - public override async Task ToAmino() - { - var aminoMsg = new AminoMsg("cosmos-sdk/MsgWithdrawDelegationReward"); - // order of properties must be sorted for amino signing!! - aminoMsg.Value = new - { - delegator_address = DelegatorAddress, - validator_address = ValidatorAddress, - }; - - return aminoMsg; - } -} - -/// -/// MsgWithdrawValidatorCommission withdraws the full commission to the validator address. -/// -public class MsgWithdrawValidatorCommission : MsgBase -{ - public override string MsgType { get; } = MsgGrantAuthorization.MsgWithdrawValidatorCommission; - - public string ValidatorAddress { get; set; } - - - public MsgWithdrawValidatorCommission() { } - - public MsgWithdrawValidatorCommission(string validatorAddress) - { - ValidatorAddress = validatorAddress; - } - - public override async Task ToProto() - { - var msgWithdrawValidatorCommission = new Cosmos.Distribution.V1Beta1.MsgWithdrawValidatorCommission() - { - ValidatorAddress = ValidatorAddress, - }; - - return msgWithdrawValidatorCommission; - } - - public override async Task ToAmino() - { - var aminoMsg = new AminoMsg("cosmos-sdk/MsgWithdrawValidatorCommission"); - // order of properties must be sorted for amino signing!! - aminoMsg.Value = new - { - validator_address = ValidatorAddress, - }; - - return aminoMsg; - } -} - -/// -/// MsgFundCommunityPool allows an account to directly fund the community pool. -/// -public class MsgFundCommunityPool : MsgBase -{ - public override string MsgType { get; } = MsgGrantAuthorization.MsgFundCommunityPool; - - public string Depositor { get; set; } - - public Coin[] Amount { get; set; } - - - public MsgFundCommunityPool() { } - - public MsgFundCommunityPool(string depositor, Coin[] amount) - { - Depositor = depositor; - Amount = amount; - } - - public override async Task ToProto() - { - var msgFundCommunityPool = new Cosmos.Distribution.V1Beta1.MsgFundCommunityPool() - { - Depositor = Depositor, - - }; - if (Amount != null) - { - msgFundCommunityPool.Amount.Add(Amount.Select(c => new Cosmos.Base.V1Beta1.Coin() { Amount = c.Amount, Denom = c.Denom }).ToArray()); - } - - return msgFundCommunityPool; - } - - public override async Task ToAmino() - { - var aminoMsg = new AminoMsg("cosmos-sdk/MsgFundCommunityPool"); - // order of properties must be sorted for amino signing!! - aminoMsg.Value = new - { - depositor = Depositor, - amount = Amount - }; - - return aminoMsg; - } -} \ No newline at end of file diff --git a/src/Tx/DistributionTx.cs b/src/Tx/DistributionTx.cs index 1ef0e144..ca110011 100644 --- a/src/Tx/DistributionTx.cs +++ b/src/Tx/DistributionTx.cs @@ -14,7 +14,7 @@ internal DistributionTx(TxClient tx) /// /// /// - public async Task> FundCommunityPool(Cosmos.Distribution.V1Beta1.MsgFundCommunityPool msg, TxOptions? txOptions = null) + public async Task> FundCommunityPool(Cosmos.Distribution.V1Beta1.MsgFundCommunityPool msg, TxOptions txOptions = null) { var txResult = await _tx.Broadcast(msg, txOptions); return new SingleSecretTx(txResult); @@ -26,7 +26,7 @@ internal DistributionTx(TxClient tx) /// /// /// - public async Task> SetWithdrawAddress(Cosmos.Distribution.V1Beta1.MsgSetWithdrawAddress msg, TxOptions? txOptions = null) + public async Task> SetWithdrawAddress(Cosmos.Distribution.V1Beta1.MsgSetWithdrawAddress msg, TxOptions txOptions = null) { var txResult = await _tx.Broadcast(msg, txOptions); return new SingleSecretTx(txResult); @@ -38,7 +38,7 @@ internal DistributionTx(TxClient tx) /// /// /// - public async Task> WithdrawDelegatorReward(Cosmos.Distribution.V1Beta1.MsgWithdrawDelegatorReward msg, TxOptions? txOptions = null) + public async Task> WithdrawDelegatorReward(Cosmos.Distribution.V1Beta1.MsgWithdrawDelegatorReward msg, TxOptions txOptions = null) { var txResult = await _tx.Broadcast(msg, txOptions); return new SingleSecretTx(txResult); @@ -50,7 +50,7 @@ internal DistributionTx(TxClient tx) /// /// /// - public async Task> WithdrawValidatorCommission(Cosmos.Distribution.V1Beta1.MsgWithdrawValidatorCommission msg, TxOptions? txOptions = null) + public async Task> WithdrawValidatorCommission(Cosmos.Distribution.V1Beta1.MsgWithdrawValidatorCommission msg, TxOptions txOptions = null) { var txResult = await _tx.Broadcast(msg, txOptions); return new SingleSecretTx(txResult); diff --git a/src/Tx/Evidence.Msg.cs b/src/Tx/Evidence.Msg.cs deleted file mode 100644 index 39fa5a41..00000000 --- a/src/Tx/Evidence.Msg.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace SecretNET.Tx; - -/// -/// MsgSubmitEvidence represents a message that supports submitting arbitrary Evidence of misbehavior such as equivocation or counterfactual signing. -/// -public class MsgSubmitEvidence : MsgBase -{ - public override string MsgType { get; } = MsgGrantAuthorization.MsgSubmitEvidence; - - public string Submitter { get; set; } - - public Any Evidence { get; set; } - - public MsgSubmitEvidence() { } - - public MsgSubmitEvidence(string submitter, Any evidence) - { - Submitter = submitter; - Evidence = evidence; - } - - public override async Task ToProto() - { - throw new NotImplementedException("MsgSubmitEvidence ToProto is not implemented."); - } - - public override async Task ToAmino() - { - throw new NotImplementedException("MsgSubmitEvidence ToAmino is not implemented."); - } -} diff --git a/src/Tx/EvidenceTx.cs b/src/Tx/EvidenceTx.cs index 3823a28e..6da237fe 100644 --- a/src/Tx/EvidenceTx.cs +++ b/src/Tx/EvidenceTx.cs @@ -16,7 +16,7 @@ internal EvidenceTx(TxClient tx) /// /// /// - public async Task> SubmitEvidence(Cosmos.Evidence.V1Beta1.MsgSubmitEvidence msg, TxOptions? txOptions = null) + public async Task> SubmitEvidence(Cosmos.Evidence.V1Beta1.MsgSubmitEvidence msg, TxOptions txOptions = null) { var txResult = await _tx.Broadcast(msg, txOptions); return new SingleSecretTx(txResult); diff --git a/src/Tx/Feegrant.Msg.cs b/src/Tx/Feegrant.Msg.cs deleted file mode 100644 index 084ed8c3..00000000 --- a/src/Tx/Feegrant.Msg.cs +++ /dev/null @@ -1,110 +0,0 @@ -namespace SecretNET.Tx; - -/// -/// MsgGrantAllowance adds permission for Grantee to spend up to Allowance of fees from the account of Granter. -/// -public class MsgGrantAllowance : MsgBase -{ - public override string MsgType { get; } = MsgGrantAuthorization.MsgGrantAllowance; - - /// - /// granter is the address of the user granting an allowance of their funds. - /// - public string Granter { get; set; } - - /// - /// grantee is the address of the user being granted an allowance of another user's funds. - /// - public string Grantee { get; set; } - - /// - /// allowance can be any of basic and filtered fee allowance. - /// - public Any Allowance { get; set; } - - public MsgGrantAllowance() { } - - public MsgGrantAllowance(string granter, string grantee, Any allowance) - { - Granter = granter; - Grantee = grantee; - Allowance = allowance; - } - - - public override async Task ToProto() - { - var msgGrantAllowance = new Cosmos.Feegrant.V1Beta1.MsgGrantAllowance() - { - Granter = Granter, - Grantee = Grantee, - Allowance = Allowance, - }; - - return msgGrantAllowance; - } - - public override async Task ToAmino() - { - var aminoMsg = new AminoMsg("cosmos-sdk/MsgGrantAllowance"); - // order of properties must be sorted for amino signing!! - aminoMsg.Value = new - { - granter = Granter, - grantee = Grantee, - }; - - return aminoMsg; - } -} - -/// -/// MsgRevokeAllowance removes any existing Allowance from Granter to Grantee. -/// -public class MsgRevokeAllowance : MsgBase -{ - public override string MsgType { get; } = MsgGrantAuthorization.MsgRevokeAllowance; - - /// - /// granter is the address of the user granting an allowance of their funds. - /// - public string Granter { get; set; } - - /// - /// grantee is the address of the user being granted an allowance of another user's funds. - /// - public string Grantee { get; set; } - - public MsgRevokeAllowance() { } - - public MsgRevokeAllowance(string granter, string grantee, Any allowance) - { - Granter = granter; - Grantee = grantee; - } - - - public override async Task ToProto() - { - var msgRevokeAllowance = new Cosmos.Feegrant.V1Beta1.MsgRevokeAllowance() - { - Granter = Granter, - Grantee = Grantee, - }; - - return msgRevokeAllowance; - } - - public override async Task ToAmino() - { - var aminoMsg = new AminoMsg("cosmos-sdk/MsgRevokeAllowance"); - // order of properties must be sorted for amino signing!! - aminoMsg.Value = new - { - granter = Granter, - grantee = Grantee, - }; - - return aminoMsg; - } -} \ No newline at end of file diff --git a/src/Tx/FeegrantTx.cs b/src/Tx/FeegrantTx.cs index 5da79891..442cbd7d 100644 --- a/src/Tx/FeegrantTx.cs +++ b/src/Tx/FeegrantTx.cs @@ -15,7 +15,7 @@ internal FeegrantTx(TxClient tx) /// /// /// - public async Task> GrantAllowance(Cosmos.Feegrant.V1Beta1.MsgGrantAllowance msg, TxOptions? txOptions = null) + public async Task> GrantAllowance(Cosmos.Feegrant.V1Beta1.MsgGrantAllowance msg, TxOptions txOptions = null) { var txResult = await _tx.Broadcast(msg, txOptions); return new SingleSecretTx(txResult); @@ -27,7 +27,7 @@ internal FeegrantTx(TxClient tx) /// /// /// - public async Task> RevokeAllowance(Cosmos.Feegrant.V1Beta1.MsgRevokeAllowance msg, TxOptions? txOptions = null) + public async Task> RevokeAllowance(Cosmos.Feegrant.V1Beta1.MsgRevokeAllowance msg, TxOptions txOptions = null) { var txResult = await _tx.Broadcast(msg, txOptions); return new SingleSecretTx(txResult); diff --git a/src/Tx/Gov.Msg.cs b/src/Tx/Gov.Msg.cs deleted file mode 100644 index 3ebd510e..00000000 --- a/src/Tx/Gov.Msg.cs +++ /dev/null @@ -1,310 +0,0 @@ -namespace SecretNET.Tx; - -/// -/// ProposalStatus enumerates the valid statuses of a proposal. -/// -public enum ProposalStatus -{ - /// - /// PROPOSAL_STATUS_UNSPECIFIED defines the default propopsal status. - /// - PROPOSAL_STATUS_UNSPECIFIED = 0, - - /// - /// PROPOSAL_STATUS_DEPOSIT_PERIOD defines a proposal status during the deposit period. - /// - PROPOSAL_STATUS_DEPOSIT_PERIOD = 1, - - /// - /// PROPOSAL_STATUS_VOTING_PERIOD defines a proposal status during the voting period. - /// - PROPOSAL_STATUS_VOTING_PERIOD = 2, - - /// - /// PROPOSAL_STATUS_PASSED defines a proposal status of a proposal that has passed. - /// - PROPOSAL_STATUS_PASSED = 3, - - /// - /// PROPOSAL_STATUS_REJECTED defines a proposal status of a proposal that has been rejected. - /// - PROPOSAL_STATUS_REJECTED = 4, - - /// - /// PROPOSAL_STATUS_FAILED defines a proposal status of a proposal that has failed. - /// - PROPOSAL_STATUS_FAILED = 5, - - UNRECOGNIZED = -1, -} - -public enum ProposalType -{ - None = 0, - TextProposal = 1, - CommunityPoolSpendProposal = 2, - - /// - /// @see {@link https://docs.scrt.network/guides/governance} for possible subspaces, keys and values. - /// - ParameterChangeProposal = 3, - - /// - /// Not supported with Amino signer. - /// - ClientUpdateProposal = 4, - - /// - /// Not supported with Amino signer. - /// - UpgradeProposal = 5, - - SoftwareUpgradeProposal = 6, - CancelSoftwareUpgradeProposal = 7, -} - -/// -/// MsgSubmitProposal defines an sdk.Msg type that supports submitting arbitrary proposal Content. -/// -public class MsgSubmitProposal : MsgBase -{ - /// - /// Only following message types allowed: - /// - cosmos.gov.v1beta1.gov.TextProposal - /// - cosmos.distribution.v1beta1.CommunityPoolSpendProposal - /// - cosmos.params.v1beta1.ParameterChangeProposal - /// - ibc.core.client.v1.ClientUpdateProposal - /// - ibc.core.client.v1.UpgradeProposal - /// - cosmos.upgrade.v1beta1.SoftwareUpgradeProposal - /// - cosmos.upgrade.v1beta1.CancelSoftwareUpgradeProposal - /// - private IMessage _content = null; - - public override string MsgType { get; } = MsgGrantAuthorization.MsgSubmitProposal; - - public ProposalType Type { get; set; } - - public Coin[] InitialDeposit { get; set; } - - public string Proposer { get; set; } - - - public MsgSubmitProposal() { } - - public MsgSubmitProposal(ProposalType type, Coin[] initialDeposit, string proposer) - { - Type = type; - InitialDeposit = initialDeposit; - Proposer = proposer; - } - - - public override async Task ToProto() - { - if (Type == ProposalType.TextProposal) - { - _content = new Cosmos.Gov.V1Beta1.TextProposal(); - } - else if (Type == ProposalType.CommunityPoolSpendProposal) - { - _content = new Cosmos.Distribution.V1Beta1.CommunityPoolSpendProposal(); - } - else if (Type == ProposalType.ParameterChangeProposal) - { - _content = new Cosmos.Params.V1Beta1.ParameterChangeProposal(); - } - else if (Type == ProposalType.ClientUpdateProposal) - { - _content = new Ibc.Core.Client.V1.ClientUpdateProposal(); - } - else if (Type == ProposalType.UpgradeProposal) - { - _content = new Ibc.Core.Client.V1.UpgradeProposal(); - } - else if (Type == ProposalType.SoftwareUpgradeProposal) - { - _content = new Cosmos.Upgrade.V1Beta1.SoftwareUpgradeProposal(); - } - else if (Type == ProposalType.CancelSoftwareUpgradeProposal) - { - _content = new Cosmos.Upgrade.V1Beta1.CancelSoftwareUpgradeProposal(); - } - - if (_content != null) - { - var msgSubmitProposal = new Cosmos.Gov.V1Beta1.MsgSubmitProposal() - { - Content = Any.Pack(_content,""), - Proposer = Proposer - }; - - if (InitialDeposit != null) - { - msgSubmitProposal.InitialDeposit.Add(InitialDeposit.Select(c => new Cosmos.Base.V1Beta1.Coin() { Amount = c.Amount, Denom = c.Denom }).ToArray()); - } - - return msgSubmitProposal; - } - return null; - } - - public override async Task ToAmino() - { - throw new NotImplementedException("MsgSubmitProposal ToAmino is not implemented."); - } -} - -/// -/// MsgVote defines a message to cast a vote. -/// -public class MsgVote : MsgBase -{ - public override string MsgType { get; } = MsgGrantAuthorization.MsgVote; - - public Cosmos.Gov.V1Beta1.VoteOption Option { get; set; } - - public string Voter { get; set; } - - public ulong ProposalId { get; set; } - - - public MsgVote() { } - - public MsgVote(Cosmos.Gov.V1Beta1.VoteOption option, string voter, ulong proposalId) - { - Option = option; - Voter = voter; - ProposalId = proposalId; - } - - public override async Task ToProto() - { - var msgVote = new Cosmos.Gov.V1Beta1.MsgVote() - { - Voter = Voter, - ProposalId = ProposalId, - Option = Option - }; - return msgVote; - } - - public override async Task ToAmino() - { - var aminoMsg = new AminoMsg("cosmos-sdk/MsgVote"); - // order of properties must be sorted for amino signing!! - aminoMsg.Value = new - { - option = (byte)Option, - proposal_id = ProposalId, - voter = Voter - }; - return aminoMsg; - } -} - -/// -/// MsgVoteWeighted defines a message to cast a vote, with an option to split the vote. -/// -public class MsgVoteWeighted : MsgBase -{ - public override string MsgType { get; } = MsgGrantAuthorization.MsgVoteWeighted; - - public Cosmos.Gov.V1Beta1.WeightedVoteOption[] Options { get; set; } - - public string Voter { get; set; } - - public ulong ProposalId { get; set; } - - public MsgVoteWeighted() { } - - public MsgVoteWeighted(Cosmos.Gov.V1Beta1.WeightedVoteOption[] options, string voter, ulong proposalId) - { - Options = options; - Voter = voter; - ProposalId = proposalId; - } - - public override async Task ToProto() - { - var msgVoteWeighted = new Cosmos.Gov.V1Beta1.MsgVoteWeighted() - { - Voter = Voter, - ProposalId = ProposalId, - }; - if (Options != null) - { - msgVoteWeighted.Options.Add(Options); - } - - return msgVoteWeighted; - } - - public override async Task ToAmino() - { - var aminoMsg = new AminoMsg("cosmos-sdk/MsgVoteWeighted"); - // order of properties must be sorted for amino signing!! - aminoMsg.Value = new - { - options = Options.Select(o => new - { - option = (byte)o.Option, - weight = o.Weight - }), - proposal_id = ProposalId, - voter = Voter - }; - return aminoMsg; - } -} - -/// -/// MsgDeposit defines a message to submit a deposit to an existing proposal. -/// -public class MsgDeposit : MsgBase -{ - public override string MsgType { get; } = MsgGrantAuthorization.MsgDeposit; - - public string Depositor { get; set; } - - public ulong ProposalId { get; set; } - - public Coin[] Amount { get; set; } - - - public MsgDeposit() { } - - public MsgDeposit(string depositor, ulong proposalId, Coin[] amount) - { - Depositor = depositor; - ProposalId = proposalId; - Amount = amount; - } - - public override async Task ToProto() - { - var msgDeposit = new Cosmos.Gov.V1Beta1.MsgDeposit() - { - Depositor = Depositor, - ProposalId = ProposalId, - }; - if (Amount != null) - { - msgDeposit.Amount.Add(Amount.Select(c => new Cosmos.Base.V1Beta1.Coin() { Amount = c.Amount, Denom = c.Denom }).ToArray()); - } - - return msgDeposit; - } - - public override async Task ToAmino() - { - var aminoMsg = new AminoMsg("cosmos-sdk/MsgVoteWeighted"); - // order of properties must be sorted for amino signing!! - aminoMsg.Value = new - { - amount = Amount, - depositor = Depositor, - proposal_id = ProposalId, - }; - return aminoMsg; - } -} \ No newline at end of file diff --git a/src/Tx/GovTx.cs b/src/Tx/GovTx.cs index 24b0639e..eb570c0e 100644 --- a/src/Tx/GovTx.cs +++ b/src/Tx/GovTx.cs @@ -15,7 +15,7 @@ internal GovTx(TxClient tx) /// /// /// - public async Task> Deposit(Cosmos.Gov.V1Beta1.MsgDeposit msg, TxOptions? txOptions = null) + public async Task> Deposit(Cosmos.Gov.V1Beta1.MsgDeposit msg, TxOptions txOptions = null) { var txResult = await _tx.Broadcast(msg, txOptions); return new SingleSecretTx(txResult); @@ -27,7 +27,7 @@ internal GovTx(TxClient tx) /// /// /// - public async Task> SubmitProposal(Cosmos.Gov.V1Beta1.MsgSubmitProposal msg, TxOptions? txOptions = null) + public async Task> SubmitProposal(Cosmos.Gov.V1Beta1.MsgSubmitProposal msg, TxOptions txOptions = null) { var txResult = await _tx.Broadcast(msg, txOptions); return new SingleSecretTx(txResult); @@ -39,7 +39,7 @@ internal GovTx(TxClient tx) /// /// /// - public async Task> Vote(Cosmos.Gov.V1Beta1.MsgVote msg, TxOptions? txOptions = null) + public async Task> Vote(Cosmos.Gov.V1Beta1.MsgVote msg, TxOptions txOptions = null) { var txResult = await _tx.Broadcast(msg, txOptions); return new SingleSecretTx(txResult); @@ -51,7 +51,7 @@ internal GovTx(TxClient tx) /// /// /// - public async Task> VoteWeighted(Cosmos.Gov.V1Beta1.MsgVoteWeighted msg, TxOptions? txOptions = null) + public async Task> VoteWeighted(Cosmos.Gov.V1Beta1.MsgVoteWeighted msg, TxOptions txOptions = null) { var txResult = await _tx.Broadcast(msg, txOptions); return new SingleSecretTx(txResult); diff --git a/src/Tx/IBC_Channel.Msg.cs b/src/Tx/IBC_Channel.Msg.cs deleted file mode 100644 index 6bdc992c..00000000 --- a/src/Tx/IBC_Channel.Msg.cs +++ /dev/null @@ -1,171 +0,0 @@ -namespace SecretNET.Tx; - -/// -/// MsgRecvPacket receives incoming IBC packet -/// -public class MsgRecvPacket : MsgBase -{ - public override string MsgType { get; } = MsgGrantAuthorization.MsgRecvPacket; - - public override Task ToProto() - { - throw new NotImplementedException("MsgSubmitEvidence ToAmino is not implemented."); - } - public override Task ToAmino() - { - throw new NotImplementedException("MsgSubmitEvidence ToProto is not implemented."); - } -} - -/// -/// MsgTimeout receives timed-out packet -/// -public class MsgTimeout : MsgBase -{ - public override string MsgType { get; } = MsgGrantAuthorization.MsgTimeout; - - public override Task ToProto() - { - throw new NotImplementedException("MsgTimeout ToAmino is not implemented."); - } - public override Task ToAmino() - { - throw new NotImplementedException("MsgTimeout ToProto is not implemented."); - } -} - -/// -/// MsgTimeoutOnClose timed-out packet upon counterparty channel closure. -/// -public class MsgTimeoutOnClose : MsgBase -{ - public override string MsgType { get; } = MsgGrantAuthorization.MsgTimeoutOnClose; - - public override Task ToProto() - { - throw new NotImplementedException("MsgTimeoutOnClose ToAmino is not implemented."); - } - public override Task ToAmino() - { - throw new NotImplementedException("MsgTimeoutOnClose ToProto is not implemented."); - } -} - -/// -/// MsgChannelOpenInit defines an sdk.Msg to initialize a channel handshake. It is called by a relayer on Chain A. -/// -public class MsgChannelOpenInit : MsgBase -{ - public override string MsgType { get; } = MsgGrantAuthorization.MsgChannelOpenInit; - - public override Task ToProto() - { - throw new NotImplementedException("MsgChannelOpenInit ToAmino is not implemented."); - } - public override Task ToAmino() - { - throw new NotImplementedException("MsgChannelOpenInit ToProto is not implemented."); - } -} - -/// -/// MsgAcknowledgement receives incoming IBC acknowledgement -/// -public class MsgAcknowledgement : MsgBase -{ - public override string MsgType { get; } = MsgGrantAuthorization.MsgAcknowledgement; - - public override Task ToProto() - { - throw new NotImplementedException("MsgAcknowledgement ToAmino is not implemented."); - } - public override Task ToAmino() - { - throw new NotImplementedException("MsgAcknowledgement ToProto is not implemented."); - } -} - -/// -/// MsgChannelOpenInit defines a msg sent by a Relayer to try to open a channel on Chain B. -/// -public class MsgChannelOpenTry : MsgBase -{ - public override string MsgType { get; } = MsgGrantAuthorization.MsgChannelOpenTry; - - public override Task ToProto() - { - throw new NotImplementedException("MsgChannelOpenTry ToAmino is not implemented."); - } - public override Task ToAmino() - { - throw new NotImplementedException("MsgChannelOpenTry ToProto is not implemented."); - } -} - -/// -/// MsgChannelOpenAck defines a msg sent by a Relayer to Chain A to acknowledge the change of channel state to TRYOPEN on Chain B. -/// -public class MsgChannelOpenAck : MsgBase -{ - public override string MsgType { get; } = MsgGrantAuthorization.MsgChannelOpenAck; - - public override Task ToProto() - { - throw new NotImplementedException("MsgChannelOpenAck ToAmino is not implemented."); - } - public override Task ToAmino() - { - throw new NotImplementedException("MsgChannelOpenAck ToProto is not implemented."); - } -} - -/// -/// MsgChannelOpenConfirm defines a msg sent by a Relayer to Chain B to acknowledge the change of channel state to OPEN on Chain A. -/// -public class MsgChannelOpenConfirm : MsgBase -{ - public override string MsgType { get; } = MsgGrantAuthorization.MsgChannelOpenConfirm; - - public override Task ToProto() - { - throw new NotImplementedException("MsgChannelOpenConfirm ToAmino is not implemented."); - } - public override Task ToAmino() - { - throw new NotImplementedException("MsgChannelOpenConfirm ToProto is not implemented."); - } -} - -/// -/// MsgChannelCloseInit defines a msg sent by a Relayer to Chain A to close a channel with Chain B. -/// -public class MsgChannelCloseInit : MsgBase -{ - public override string MsgType { get; } = MsgGrantAuthorization.MsgChannelCloseInit; - - public override Task ToProto() - { - throw new NotImplementedException("MsgChannelCloseInit ToAmino is not implemented."); - } - public override Task ToAmino() - { - throw new NotImplementedException("MsgChannelCloseInit ToProto is not implemented."); - } -} - -/// -/// MsgChannelCloseConfirm defines a msg sent by a Relayer to Chain B to acknowledge the change of channel state to CLOSED on Chain A. -/// -public class MsgChannelCloseConfirm : MsgBase -{ - public override string MsgType { get; } = MsgGrantAuthorization.MsgChannelCloseConfirm; - - public override Task ToProto() - { - throw new NotImplementedException("MsgChannelCloseConfirm ToAmino is not implemented."); - } - public override Task ToAmino() - { - throw new NotImplementedException("MsgChannelCloseConfirm ToProto is not implemented."); - } -} \ No newline at end of file diff --git a/src/Tx/IBC_ChannelTx.cs b/src/Tx/IBC_ChannelTx.cs index 349db084..189145ca 100644 --- a/src/Tx/IBC_ChannelTx.cs +++ b/src/Tx/IBC_ChannelTx.cs @@ -9,61 +9,61 @@ internal IbcChannelTx(TxClient tx) _tx = tx; } - public async Task> ChannelOpenInit(Ibc.Core.Channel.V1.MsgChannelOpenInit msg, TxOptions? txOptions = null) + public async Task> ChannelOpenInit(Ibc.Core.Channel.V1.MsgChannelOpenInit msg, TxOptions txOptions = null) { var txResult = await _tx.Broadcast(msg, txOptions); return new SingleSecretTx(txResult); } - public async Task> ChannelOpenTry(Ibc.Core.Channel.V1.MsgChannelOpenTry msg, TxOptions? txOptions = null) + public async Task> ChannelOpenTry(Ibc.Core.Channel.V1.MsgChannelOpenTry msg, TxOptions txOptions = null) { var txResult = await _tx.Broadcast(msg, txOptions); return new SingleSecretTx(txResult); } - public async Task> ChannelOpenAck(Ibc.Core.Channel.V1.MsgChannelOpenAck msg, TxOptions? txOptions = null) + public async Task> ChannelOpenAck(Ibc.Core.Channel.V1.MsgChannelOpenAck msg, TxOptions txOptions = null) { var txResult = await _tx.Broadcast(msg, txOptions); return new SingleSecretTx(txResult); } - public async Task> ChannelOpenConfirm(Ibc.Core.Channel.V1.MsgChannelOpenConfirm msg, TxOptions? txOptions = null) + public async Task> ChannelOpenConfirm(Ibc.Core.Channel.V1.MsgChannelOpenConfirm msg, TxOptions txOptions = null) { var txResult = await _tx.Broadcast(msg, txOptions); return new SingleSecretTx(txResult); } - public async Task> ChannelCloseInit(Ibc.Core.Channel.V1.MsgChannelCloseInit msg, TxOptions? txOptions = null) + public async Task> ChannelCloseInit(Ibc.Core.Channel.V1.MsgChannelCloseInit msg, TxOptions txOptions = null) { var txResult = await _tx.Broadcast(msg, txOptions); return new SingleSecretTx(txResult); } - public async Task> ChannelCloseConfirm(Ibc.Core.Channel.V1.MsgChannelCloseConfirm msg, TxOptions? txOptions = null) + public async Task> ChannelCloseConfirm(Ibc.Core.Channel.V1.MsgChannelCloseConfirm msg, TxOptions txOptions = null) { var txResult = await _tx.Broadcast(msg, txOptions); return new SingleSecretTx(txResult); } - public async Task> RecvPacket(Ibc.Core.Channel.V1.MsgRecvPacket msg, TxOptions? txOptions = null) + public async Task> RecvPacket(Ibc.Core.Channel.V1.MsgRecvPacket msg, TxOptions txOptions = null) { var txResult = await _tx.Broadcast(msg, txOptions); return new SingleSecretTx(txResult); } - public async Task> Timeout(Ibc.Core.Channel.V1.MsgTimeout msg, TxOptions? txOptions = null) + public async Task> Timeout(Ibc.Core.Channel.V1.MsgTimeout msg, TxOptions txOptions = null) { var txResult = await _tx.Broadcast(msg, txOptions); return new SingleSecretTx(txResult); } - public async Task> TimeoutOnClose(Ibc.Core.Channel.V1.MsgTimeoutOnClose msg, TxOptions? txOptions = null) + public async Task> TimeoutOnClose(Ibc.Core.Channel.V1.MsgTimeoutOnClose msg, TxOptions txOptions = null) { var txResult = await _tx.Broadcast(msg, txOptions); return new SingleSecretTx(txResult); } - public async Task> Acknowledgement(Ibc.Core.Channel.V1.MsgAcknowledgement msg, TxOptions? txOptions = null) + public async Task> Acknowledgement(Ibc.Core.Channel.V1.MsgAcknowledgement msg, TxOptions txOptions = null) { var txResult = await _tx.Broadcast(msg, txOptions); return new SingleSecretTx(txResult); diff --git a/src/Tx/IBC_Client.Msg.cs b/src/Tx/IBC_Client.Msg.cs deleted file mode 100644 index 8855c2bf..00000000 --- a/src/Tx/IBC_Client.Msg.cs +++ /dev/null @@ -1,54 +0,0 @@ -namespace SecretNET.Tx; - -/// -/// MsgUpdateClient defines an sdk.Msg to update a IBC client state using the given header. -/// -public class MsgUpgradeClient : MsgBase -{ - public override string MsgType { get; } = MsgGrantAuthorization.MsgUpgradeClient; - - public override Task ToProto() - { - throw new NotImplementedException("MsgUpgradeClient ToAmino is not implemented."); - } - - public override Task ToAmino() - { - throw new NotImplementedException("MsgUpgradeClient ToProto is not implemented."); - } -} - -/// -/// MsgSubmitMisbehaviour defines an sdk.Msg type that submits Evidence for light client misbehaviour. -/// -public class MsgSubmitMisbehaviour : MsgBase -{ - public override string MsgType { get; } = MsgGrantAuthorization.MsgSubmitMisbehaviour; - - public override Task ToProto() - { - throw new NotImplementedException("MsgSubmitMisbehaviour ToAmino is not implemented."); - } - public override Task ToAmino() - { - throw new NotImplementedException("MsgSubmitMisbehaviour ToProto is not implemented."); - } -} - -/// -/// MsgCreateClient defines a message to create an IBC client -/// -public class MsgCreateClient : MsgBase -{ - public override string MsgType { get; } = MsgGrantAuthorization.MsgCreateClient; - - public override Task ToProto() - { - throw new NotImplementedException("MsgCreateClient ToAmino is not implemented."); - } - public override Task ToAmino() - { - throw new NotImplementedException("MsgCreateClient ToProto is not implemented."); - } -} - diff --git a/src/Tx/IBC_ClientTx.cs b/src/Tx/IBC_ClientTx.cs index f6d26db6..2ab9e4e2 100644 --- a/src/Tx/IBC_ClientTx.cs +++ b/src/Tx/IBC_ClientTx.cs @@ -9,26 +9,26 @@ internal IbcClientTx(TxClient tx) _tx = tx; } - public async Task> CreateClient(Ibc.Core.Client.V1.MsgCreateClient msg, TxOptions? txOptions = null) + public async Task> CreateClient(Ibc.Core.Client.V1.MsgCreateClient msg, TxOptions txOptions = null) { var txResult = await _tx.Broadcast(msg, txOptions); return new SingleSecretTx(txResult); } - public async Task> UpdateClient(Ibc.Core.Client.V1.MsgUpdateClient msg, TxOptions? txOptions = null) + public async Task> UpdateClient(Ibc.Core.Client.V1.MsgUpdateClient msg, TxOptions txOptions = null) { var txResult = await _tx.Broadcast(msg, txOptions); return new SingleSecretTx(txResult); } - public async Task> UpgradeClient(Ibc.Core.Client.V1.MsgUpgradeClient msg, TxOptions? txOptions = null) + public async Task> UpgradeClient(Ibc.Core.Client.V1.MsgUpgradeClient msg, TxOptions txOptions = null) { var txResult = await _tx.Broadcast(msg, txOptions); return new SingleSecretTx(txResult); } - public async Task> SubmitMisbehaviour(Ibc.Core.Client.V1.MsgSubmitMisbehaviour msg, TxOptions? txOptions = null) + public async Task> SubmitMisbehaviour(Ibc.Core.Client.V1.MsgSubmitMisbehaviour msg, TxOptions txOptions = null) { var txResult = await _tx.Broadcast(msg, txOptions); return new SingleSecretTx(txResult); diff --git a/src/Tx/IBC_Connection.Msg.cs b/src/Tx/IBC_Connection.Msg.cs deleted file mode 100644 index 9e618ee1..00000000 --- a/src/Tx/IBC_Connection.Msg.cs +++ /dev/null @@ -1,38 +0,0 @@ -namespace SecretNET.Tx; - -/// -/// MsgConnectionOpenAck defines a msg sent by a Relayer to Chain A to acknowledge the change of connection state to TRYOPEN on Chain B. -/// -public class MsgConnectionOpenAck : MsgBase -{ - public override string MsgType { get; } = MsgGrantAuthorization.MsgConnectionOpenAck; - - public override Task ToProto() - { - throw new NotImplementedException("MsgConnectionOpenAck ToAmino is not implemented."); - } - public override Task ToAmino() - { - throw new NotImplementedException("MsgConnectionOpenAck ToProto is not implemented."); - } -} - -/// -/// MsgConnectionOpenConfirm defines a msg sent by a Relayer to Chain B to acknowledge the change of connection state to OPEN on Chain A. -/// -public class MsgConnectionOpenConfirm : MsgBase -{ - public override string MsgType { get; } = MsgGrantAuthorization.MsgConnectionOpenConfirm; - - public override Task ToProto() - { - throw new NotImplementedException("MsgConnectionOpenConfirm ToAmino is not implemented."); - } - public override Task ToAmino() - { - throw new NotImplementedException("MsgConnectionOpenConfirm ToProto is not implemented."); - } -} - - - diff --git a/src/Tx/IBC_ConnectionTx.cs b/src/Tx/IBC_ConnectionTx.cs index 30ccff04..c58396ca 100644 --- a/src/Tx/IBC_ConnectionTx.cs +++ b/src/Tx/IBC_ConnectionTx.cs @@ -9,26 +9,26 @@ internal IbcConnectionTx(TxClient tx) _tx = tx; } - public async Task> ConnectionOpenInit(Ibc.Core.Connection.V1.MsgConnectionOpenInit msg, TxOptions? txOptions = null) + public async Task> ConnectionOpenInit(Ibc.Core.Connection.V1.MsgConnectionOpenInit msg, TxOptions txOptions = null) { var txResult = await _tx.Broadcast(msg, txOptions); return new SingleSecretTx(txResult); } - public async Task> ConnectionOpenTry(Ibc.Core.Connection.V1.MsgConnectionOpenTry msg, TxOptions? txOptions = null) + public async Task> ConnectionOpenTry(Ibc.Core.Connection.V1.MsgConnectionOpenTry msg, TxOptions txOptions = null) { var txResult = await _tx.Broadcast(msg, txOptions); return new SingleSecretTx(txResult); } - public async Task> ConnectionOpenAck(Ibc.Core.Connection.V1.MsgConnectionOpenAck msg, TxOptions? txOptions = null) + public async Task> ConnectionOpenAck(Ibc.Core.Connection.V1.MsgConnectionOpenAck msg, TxOptions txOptions = null) { var txResult = await _tx.Broadcast(msg, txOptions); return new SingleSecretTx(txResult); } - public async Task> ConnectionOpenConfirm(Ibc.Core.Connection.V1.MsgConnectionOpenConfirm msg, TxOptions? txOptions = null) + public async Task> ConnectionOpenConfirm(Ibc.Core.Connection.V1.MsgConnectionOpenConfirm msg, TxOptions txOptions = null) { var txResult = await _tx.Broadcast(msg, txOptions); return new SingleSecretTx(txResult); diff --git a/src/Tx/IBC_Transfer.Msg.cs b/src/Tx/IBC_Transfer.Msg.cs deleted file mode 100644 index 537c5a03..00000000 --- a/src/Tx/IBC_Transfer.Msg.cs +++ /dev/null @@ -1,99 +0,0 @@ -using Ibc.Core.Client.V1; - -namespace SecretNET.Tx; - -/// -/// MsgTransfer defines a msg to transfer fungible tokens (i.e Coins) between -/// ICS20 enabled chains. See ICS Spec here: -/// https://github.com/cosmos/ics/tree/master/spec/ics-020-fungible-token-transfer#data-structures -/// -public class MsgTransfer : MsgBase -{ - public override string MsgType { get; } = MsgGrantAuthorization.MsgTransfer; - - /// - /// the port on which the packet will be sent - /// - public string SourcePort { get; set; } - - /// - /// the channel by which the packet will be sent - /// - public string SourceChannel { get; set; } - - /// - /// the tokens to be transferred - /// - public Coin Token { get; set; } - - /// - /// the sender address - /// - public string Sender { get; set; } - - /// - /// the recipient address on the destination chain - /// - public string Receiver { get; set; } - - /// - /// Timeout height relative to the current block height. - /// The timeout is disabled when undefined or set to 0. - /// - /// Height is a monotonically increasing data type - /// that can be compared against another Height for the purposes of updating and - /// freezing clients. - /// - /// Normally the RevisionHeight is incremented at each height while keeping - /// RevisionNumber the same. However some consensus algorithms may choose to - /// reset the height in certain conditions e.g. hard forks, state-machine - /// breaking changes In these cases, the RevisionNumber is incremented so that - /// height continues to be monitonically increasing even as the RevisionHeight gets reset - /// - public Height? TimeoutHeight { get; set; } - - /// - /// Timeout timestamp (in seconds) since Unix epoch. - /// The timeout is disabled when undefined or set to 0. - /// - public string? TimeoutTimestampSec { get; set; } - - public MsgTransfer() { } - - public MsgTransfer(string sourcePort, string sourceChannel, Coin token, string sender, string receiver, Height? timeoutHeight = null, string? timeoutTimestampSec = null) - { - SourcePort = sourcePort; - SourceChannel = sourceChannel; - Token = token; - Sender = sender; - Receiver = receiver; - TimeoutHeight = timeoutHeight; - TimeoutTimestampSec = timeoutTimestampSec; - } - - public override async Task ToProto() - { - var msgTransfer = new Ibc.Applications.Transfer.V1.MsgTransfer() - { - SourcePort = SourcePort, - SourceChannel = SourceChannel, - Token = new Cosmos.Base.V1Beta1.Coin() { Amount = Token.Amount, Denom = Token.Denom }, - Sender = Sender, - Receiver = Receiver, - TimeoutHeight = TimeoutHeight, - TimeoutTimestamp = ulong.Parse(!String.IsNullOrEmpty(TimeoutTimestampSec) ? $"{TimeoutTimestampSec}000000000" /* sec -> ns */ : "0") - }; - - return msgTransfer; - } - - public override Task ToAmino() - { - throw new NotImplementedException("MsgTransfer ToAmino is not implemented."); - } -} - - - - - diff --git a/src/Tx/IBC_TransferTx.cs b/src/Tx/IBC_TransferTx.cs index 0f3cf955..79fc4412 100644 --- a/src/Tx/IBC_TransferTx.cs +++ b/src/Tx/IBC_TransferTx.cs @@ -17,7 +17,7 @@ internal IbcTransferTx(TxClient tx) /// /// /// - public async Task> Transfer(Ibc.Applications.Transfer.V1.MsgTransfer msg, TxOptions? txOptions = null) + public async Task> Transfer(Ibc.Applications.Transfer.V1.MsgTransfer msg, TxOptions txOptions = null) { var txResult = await _tx.Broadcast(msg, txOptions); return new SingleSecretTx(txResult); diff --git a/src/Tx/Registration.Msg.cs b/src/Tx/Registration.Msg.cs deleted file mode 100644 index eeb66a1a..00000000 --- a/src/Tx/Registration.Msg.cs +++ /dev/null @@ -1,49 +0,0 @@ -namespace SecretNET.Tx; - -/// -/// RaAuthenticate defines a message to register an new node. -/// -public class RaAuthenticate : MsgBase -{ - public override string MsgType { get; } = MsgTypeNames.RaAuthenticate; - - public string Sender { get; set; } - - public byte[] Certificate { get; set; } - - public RaAuthenticate(){} - - public RaAuthenticate(string sender, byte[] certificate) - { - Sender = sender; - Certificate = certificate; - } - - public override async Task ToProto() - { - var msgTransfer = new Secret.Registration.V1Beta1.RaAuthenticate() - { - Sender = SecretNetworkClient.AddressToBytes(Sender).GetByteStringFromBase64(), - Certificate = ByteString.CopyFrom(Certificate) - }; - - return msgTransfer; - } - public override async Task ToAmino() - { - var aminoMsg = new AminoMsg("reg/authenticate"); - // order of properties must be sorted for amino signing!! - aminoMsg.Value = new - { - ra_cert = Convert.ToBase64String(Certificate), - sender = Sender - }; - - return aminoMsg; - } -} - - - - - diff --git a/src/Tx/Slashing.Msg.cs b/src/Tx/Slashing.Msg.cs deleted file mode 100644 index 4f1c76d7..00000000 --- a/src/Tx/Slashing.Msg.cs +++ /dev/null @@ -1,44 +0,0 @@ -namespace SecretNET.Tx; - -/// -/// RaAuthenticate defines a message to register an new node. -/// -public class MsgUnjail : MsgBase -{ - public override string MsgType { get; } = MsgGrantAuthorization.MsgUnjail; - - public string ValidatorAddress { get; set; } - - public MsgUnjail(){} - - public MsgUnjail(string validatorAddress) - { - ValidatorAddress = validatorAddress; - } - - public override async Task ToProto() - { - var msgUnjail = new Cosmos.Slashing.V1Beta1.MsgUnjail() - { - ValidatorAddr = ValidatorAddress, - }; - - return msgUnjail; - } - public override async Task ToAmino() - { - var aminoMsg = new AminoMsg("cosmos-sdk/MsgUnjail"); - // order of properties must be sorted for amino signing!! - aminoMsg.Value = new - { - address = ValidatorAddress - }; - - return aminoMsg; - } -} - - - - - diff --git a/src/Tx/SlashingTx.cs b/src/Tx/SlashingTx.cs index 3562b3cb..3663f5e8 100644 --- a/src/Tx/SlashingTx.cs +++ b/src/Tx/SlashingTx.cs @@ -15,7 +15,7 @@ internal SlashingTx(TxClient tx) /// /// /// - public async Task> Unjail(Cosmos.Slashing.V1Beta1.MsgUnjail msg, TxOptions? txOptions = null) + public async Task> Unjail(Cosmos.Slashing.V1Beta1.MsgUnjail msg, TxOptions txOptions = null) { var txResult = await _tx.Broadcast(msg, txOptions); return new SingleSecretTx(txResult); diff --git a/src/Tx/Staking.Msg.cs b/src/Tx/Staking.Msg.cs deleted file mode 100644 index 06f1371a..00000000 --- a/src/Tx/Staking.Msg.cs +++ /dev/null @@ -1,276 +0,0 @@ -namespace SecretNET.Tx; - -/// -/// MsgCreateValidator defines an SDK message for creating a new validator. -/// -public class MsgCreateValidator : MsgBase -{ - public override string MsgType { get; } = MsgGrantAuthorization.MsgCreateValidator; - - /// - /// Description defines a validator description. - /// - public Cosmos.Staking.V1Beta1.Description Description { get; set; } - - /// - /// CommissionRates defines the initial commission rates to be used for creating a validator. - /// - public Cosmos.Staking.V1Beta1.CommissionRates Commission { get; set; } - - /// - /// minSelfDelegation is the minimum uscrt amount that the self delegator must delegate to its validator. - /// - public string MinSelfDelegation { get; set; } - - /// - /// selfDelegatorAddress is the self-delegator, which is the owner of the validator - /// - public string DelegatorAddress { get; set; } - - - public string ValidatorAddress { get; set; } - - /// - /// representation of the validator's ed25519 pubkey (32 bytes). - /// - public byte[] Pubkey { get; set; } - - /// - /// the tokens to be transferred - /// - public Coin InitialDelegation { get; set; } - - public MsgCreateValidator(){} - - public MsgCreateValidator( - Cosmos.Staking.V1Beta1.Description description, - Cosmos.Staking.V1Beta1.CommissionRates commission, - string minSelfDelegation, string delegatorAddress, string validatorAddress, byte[] pubkey, - Coin initialDelegation) - { - Description = description; - Commission = commission; - MinSelfDelegation = minSelfDelegation; - DelegatorAddress = delegatorAddress; - ValidatorAddress = validatorAddress; - Pubkey = pubkey; - InitialDelegation = initialDelegation; - } - - public override async Task ToProto() - { - var msgCreateValidator = new Cosmos.Staking.V1Beta1.MsgCreateValidator() - { - Description = Description, - Commission = Commission, - MinSelfDelegation = MinSelfDelegation, - DelegatorAddress = DelegatorAddress, - ValidatorAddress = ValidatorAddress, - }; - if (Pubkey != null && Pubkey.Length == 32) - { - msgCreateValidator.Pubkey = Any.Pack(new Cosmos.Crypto.Ed25519.PubKey() - { - Key = Pubkey.GetByteStringFromBase64(), - },""); - } - if (InitialDelegation != null) - { - msgCreateValidator.Value = new Cosmos.Base.V1Beta1.Coin() { Amount = InitialDelegation.Amount, Denom = InitialDelegation.Denom }; - } - - return msgCreateValidator; - } - public override async Task ToAmino() - { - throw new NotImplementedException("MsgCreateValidator ToAmino is not implemented."); - } -} - -/// -/// MsgEditValidator defines an SDK message for editing an existing validator. -/// -public class MsgEditValidator : MsgBase -{ - public override string MsgType { get; } = MsgGrantAuthorization.MsgEditValidator; - - public string ValidatorAddress { get; set; } - - /// - /// if description is provided it updates all values - /// - public Cosmos.Staking.V1Beta1.Description? Description { get; set; } - - /// - /// CommissionRate defines the initial commission rates to be used for creating a validator. - /// - public ulong? CommissionRate { get; set; } - - /// - /// minSelfDelegation is the minimum uscrt amount that the self delegator must delegate to its validator. - /// - public string MinSelfDelegation { get; set; } - - - public MsgEditValidator() { } - - public MsgEditValidator(string validatorAddress, string minSelfDelegation, - Cosmos.Staking.V1Beta1.Description? description = null, - ulong? commissionRate = null) - { - ValidatorAddress = validatorAddress; - MinSelfDelegation = minSelfDelegation; - Description = description; - CommissionRate = commissionRate; - } - - public override async Task ToProto() - { - var msgEditValidator = new Cosmos.Staking.V1Beta1.MsgEditValidator() - { - Description = Description, - CommissionRate = CommissionRate.GetValueOrDefault().ToString(), - MinSelfDelegation = MinSelfDelegation, - ValidatorAddress = ValidatorAddress, - }; - - return msgEditValidator; - } - public override async Task ToAmino() - { - throw new NotImplementedException("MsgEditValidator ToAmino is not implemented."); - } -} - -/// -/// MsgDelegate defines an SDK message for performing a delegation of coins from a delegator to a validator. -/// -public class MsgDelegate : MsgBase -{ - public override string MsgType { get; } = MsgGrantAuthorization.MsgDelegate; - - public string DelegatorAddress { get; set; } - - public string ValidatorAddress { get; set; } - - public Coin Amount { get; set; } - - public MsgDelegate() { } - - public MsgDelegate(string delegatorAddress, string validatorAddress, Coin amount) - { - DelegatorAddress = delegatorAddress; - ValidatorAddress = validatorAddress; - Amount = amount; - } - - public override async Task ToProto() - { - var msgDelegate = new Cosmos.Staking.V1Beta1.MsgDelegate() - { - DelegatorAddress = DelegatorAddress, - ValidatorAddress = ValidatorAddress, - }; - if (Amount != null) - { - msgDelegate.Amount = new Cosmos.Base.V1Beta1.Coin() { Amount = Amount.Amount, Denom = Amount.Denom }; - } - - return msgDelegate; - } - public override async Task ToAmino() - { - throw new NotImplementedException("MsgDelegate ToAmino is not implemented."); - } -} - -/// -/// MsgBeginRedelegate defines an SDK message for performing a redelegation of coins from a delegator and source validator to a destination validator. -/// -public class MsgBeginRedelegate : MsgBase -{ - public override string MsgType { get; } = MsgGrantAuthorization.MsgBeginRedelegate; - - public string DelegatorAddress { get; set; } - - public string ValidatorSrcAddress { get; set; } - - public string ValidatorDstAddress { get; set; } - - public Coin Amount { get; set; } - - public MsgBeginRedelegate() { } - - public MsgBeginRedelegate(string delegatorAddress, string validatorSrcAddress, string validatorDstAddress, Coin amount) - { - DelegatorAddress = delegatorAddress; - ValidatorSrcAddress = validatorSrcAddress; - ValidatorDstAddress = validatorDstAddress; - Amount = amount; - } - - public override async Task ToProto() - { - var msgBeginRedelegate = new Cosmos.Staking.V1Beta1.MsgBeginRedelegate() - { - DelegatorAddress = DelegatorAddress, - ValidatorSrcAddress = ValidatorSrcAddress, - ValidatorDstAddress = ValidatorDstAddress, - }; - if (Amount != null) - { - msgBeginRedelegate.Amount = new Cosmos.Base.V1Beta1.Coin() { Amount = Amount.Amount, Denom = Amount.Denom }; - } - - return msgBeginRedelegate; - } - public override async Task ToAmino() - { - throw new NotImplementedException("MsgBeginRedelegate ToAmino is not implemented."); - } -} - -/// -/// MsgUndelegate defines an SDK message for performing an undelegation from a delegate and a validator -/// -public class MsgUndelegate : MsgBase -{ - public override string MsgType { get; } = MsgGrantAuthorization.MsgUndelegate; - - public string DelegatorAddress { get; set; } - - public string ValidatorAddress { get; set; } - - public Coin Amount { get; set; } - - public MsgUndelegate() { } - - public MsgUndelegate(string delegatorAddress, string validatorAddress, Coin amount) - { - DelegatorAddress = delegatorAddress; - ValidatorAddress = validatorAddress; - Amount = amount; - } - - public override async Task ToProto() - { - var msgUndelegate = new Cosmos.Staking.V1Beta1.MsgUndelegate() - { - DelegatorAddress = DelegatorAddress, - ValidatorAddress = ValidatorAddress, - }; - if (Amount != null) - { - msgUndelegate.Amount = new Cosmos.Base.V1Beta1.Coin() { Amount = Amount.Amount, Denom = Amount.Denom }; - } - - return msgUndelegate; - } - public override async Task ToAmino() - { - throw new NotImplementedException("MsgUndelegate ToAmino is not implemented."); - } -} - - - diff --git a/src/Tx/StakingTx.cs b/src/Tx/StakingTx.cs index 19e7bd87..889e6ecc 100644 --- a/src/Tx/StakingTx.cs +++ b/src/Tx/StakingTx.cs @@ -15,7 +15,7 @@ internal StakingTx(TxClient tx) /// /// /// - public async Task> BeginRedelegate(Cosmos.Staking.V1Beta1.MsgBeginRedelegate msg, TxOptions? txOptions = null) + public async Task> BeginRedelegate(Cosmos.Staking.V1Beta1.MsgBeginRedelegate msg, TxOptions txOptions = null) { var txResult = await _tx.Broadcast(msg, txOptions); return new SingleSecretTx(txResult); @@ -27,7 +27,7 @@ internal StakingTx(TxClient tx) /// /// /// - public async Task> CreateValidator(Cosmos.Staking.V1Beta1.MsgCreateValidator msg, TxOptions? txOptions = null) + public async Task> CreateValidator(Cosmos.Staking.V1Beta1.MsgCreateValidator msg, TxOptions txOptions = null) { var txResult = await _tx.Broadcast(msg, txOptions); return new SingleSecretTx(txResult); @@ -39,7 +39,7 @@ internal StakingTx(TxClient tx) /// /// /// - public async Task> Delegate (Cosmos.Staking.V1Beta1.MsgDelegate msg, TxOptions? txOptions = null) + public async Task> Delegate (Cosmos.Staking.V1Beta1.MsgDelegate msg, TxOptions txOptions = null) { var txResult = await _tx.Broadcast(msg, txOptions); return new SingleSecretTx(txResult); @@ -51,7 +51,7 @@ internal StakingTx(TxClient tx) /// /// /// - public async Task> EditValidator(Cosmos.Staking.V1Beta1.MsgEditValidator msg, TxOptions? txOptions = null) + public async Task> EditValidator(Cosmos.Staking.V1Beta1.MsgEditValidator msg, TxOptions txOptions = null) { var txResult = await _tx.Broadcast(msg, txOptions); return new SingleSecretTx(txResult); @@ -63,7 +63,7 @@ internal StakingTx(TxClient tx) /// /// /// - public async Task> Undelegate(Cosmos.Staking.V1Beta1.MsgUndelegate msg, TxOptions? txOptions = null) + public async Task> Undelegate(Cosmos.Staking.V1Beta1.MsgUndelegate msg, TxOptions txOptions = null) { var txResult = await _tx.Broadcast(msg, txOptions); return new SingleSecretTx(txResult); diff --git a/src/Tx/TxClient.cs b/src/Tx/TxClient.cs index 7ee164ac..86fa3844 100644 --- a/src/Tx/TxClient.cs +++ b/src/Tx/TxClient.cs @@ -2,6 +2,11 @@ namespace SecretNET.Tx; +/// +/// Provides access to all transaction types / methods. +/// Implements the +/// +/// public class TxClient : GprcBase { private Queries _queries; @@ -67,76 +72,136 @@ internal Service.ServiceClient ServiceClient } } + /// + /// Authorization module. + /// + /// The authz. public AuthzTx Authz { get { return _authzTx; } } + /// + /// Create and broadcast transactions. + /// + /// The bank. public BankTx Bank { get { return _bankTx; } } + /// + /// Store, init and execute smart contracts. + /// + /// The compute. public ComputeTx Compute { get { return _computeTx; } } + /// + /// Crisis module. + /// + /// The crisis. public CrisisTx Crisis { get { return _crisisTx; } } + /// + /// Distribution module. + /// + /// The distribution. public DistributionTx Distribution { get { return _distributionTx; } } + /// + /// Evidence module. + /// + /// The evidence. public EvidenceTx Evidence { get { return _evidenceTx; } } + /// + /// Feegrant module. + /// + /// The feegrant. public FeegrantTx Feegrant { get { return _feegrantTx; } } + /// + /// Governance module. + /// + /// The gov. public GovTx Gov { get { return _govTx; } } + /// + /// Slashing module. + /// + /// The slashing. public SlashingTx Slashing { get { return _slashingTx; } } + /// + /// Stake module. + /// + /// The staking. public StakingTx Staking { get { return _stakingTx; } } + /// + /// Vesting module. + /// + /// The vesting. public VestingTx Vesting { get { return _vestingTx; } } + /// + /// IBC Channel module. + /// + /// The ibc channel. public IbcChannelTx IbcChannel { get { return _ibcChannelTx; } } + /// + /// IBC Client module. + /// + /// The ibc client. public IbcClientTx IbcClient { get { return _ibcClientTx; } } + /// + /// IBC Connection module. + /// + /// The ibc connection. public IbcConnectionTx IbcConnection { get { return _ibcConnectionTx; } } + /// + /// IBC Transfer module. + /// + /// The ibc transfer. public IbcTransferTx IbcTransfer { get { return _ibcTransferTx; } @@ -194,6 +259,12 @@ public async Task Broadcast(MsgBase message, TxOptions txOptions = nul return await Broadcast(new MsgBase[] { message }, txOptions); } + /// + /// Broadcasts the specified Tx / messages. + /// + /// The message. + /// The tx options. + /// SecretTx. public async Task Broadcast(IMessage message, TxOptions txOptions = null) { return await Broadcast(new MsgBase[] { new Msg(message) }, txOptions); @@ -211,6 +282,13 @@ public async Task> Broadcast(MsgBase message, TxOptions txO return await Broadcast(new MsgBase[] { message }, txOptions); } + /// + /// Broadcasts the specified Tx / messages. + /// + /// + /// The message. + /// The tx options. + /// SingleSecretTx<T>. public async Task> Broadcast(IMessage message, TxOptions txOptions = null) { return await Broadcast(new MsgBase[] { new Msg(message) }, txOptions); @@ -233,6 +311,13 @@ public async Task> Broadcast(MsgBase[] messages, TxOptions return null; } + /// + /// Broadcasts the specified Tx / messages. + /// + /// + /// The messages. + /// The tx options. + /// SingleSecretTx<T>. public async Task> Broadcast(IMessage[] messages, TxOptions txOptions = null) { var msgBaseList = new List(); @@ -263,8 +348,6 @@ private async Task BroadcastTx(byte[] TxBytes, TxOptions txOptions) var start = DateTime.Now; var txHash = SecretNET.Crypto.Hashes.SHA256(TxBytes).ToHexString().ToUpper(); - Console.WriteLine("BroadcastTx Calculated TxHash: " + txHash); - var mode = txOptions.BroadcastMode; var waitForCommit = txOptions.WaitForCommit; diff --git a/src/Tx/Vesting.Msg.cs b/src/Tx/Vesting.Msg.cs deleted file mode 100644 index a0a65d6c..00000000 --- a/src/Tx/Vesting.Msg.cs +++ /dev/null @@ -1,57 +0,0 @@ -namespace SecretNET.Tx; - -/// -/// RaAuthenticate defines a message to register an new node. -/// -public class MsgCreateVestingAccount : MsgBase -{ - public override string MsgType { get; } = MsgTypeNames.MsgCreateVestingAccount; - - public string FromAddress { get; set; } - - public string ToAddress { get; set; } - - public Coin[] Amount { get; set; } - - public long EndTime { get; set; } - - public bool Delayed { get; set; } - - public MsgCreateVestingAccount(){} - - public MsgCreateVestingAccount(string fromAddress, string toAddress, Coin[] amount, long endTime, bool delayed) - { - FromAddress = fromAddress; - ToAddress = toAddress; - Amount = amount; - EndTime = endTime; - Delayed = delayed; - } - - public override async Task ToProto() - { - var msgUnjail = new Cosmos.Vesting.V1Beta1.MsgCreateVestingAccount() - { - FromAddress = FromAddress, - ToAddress = ToAddress, - EndTime = EndTime, - Delayed = Delayed - }; - - if (Amount != null) - { - msgUnjail.Amount.Add(Amount.Select(c => new Cosmos.Base.V1Beta1.Coin() { Amount = c.Amount, Denom = c.Denom }).ToArray()); - } - - return msgUnjail; - } - public override async Task ToAmino() - { - throw new NotImplementedException("MsgCreateVestingAccount ToAmino is not implemented."); - } -} - - - - - diff --git a/src/Tx/VestingTx.cs b/src/Tx/VestingTx.cs index 45e56449..1376d89b 100644 --- a/src/Tx/VestingTx.cs +++ b/src/Tx/VestingTx.cs @@ -15,7 +15,7 @@ internal VestingTx(TxClient tx) /// /// /// - public async Task> CreateVestingAccount(Cosmos.Vesting.V1Beta1.MsgCreateVestingAccount msg, TxOptions? txOptions = null) + public async Task> CreateVestingAccount(Cosmos.Vesting.V1Beta1.MsgCreateVestingAccount msg, TxOptions txOptions = null) { var txResult = await _tx.Broadcast(msg, txOptions); return new SingleSecretTx(txResult); From 7f4f638b4fcc68c01c30e26164ef1b5b4bd27baa Mon Sep 17 00:00:00 2001 From: 0xxCodemonkey Date: Fri, 23 Sep 2022 10:35:52 +0200 Subject: [PATCH 23/31] docu --- src/Query/Queries.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Query/Queries.cs b/src/Query/Queries.cs index 82c781ad..a324e5d2 100644 --- a/src/Query/Queries.cs +++ b/src/Query/Queries.cs @@ -82,7 +82,7 @@ internal Service.ServiceClient ServiceClient } /// - /// Gets the tx. + /// Returns a transaction with a txhash. hash is a 64 character upper-case hex string. /// /// The hash. /// if set to true the client tries to decrypt the tx data (works only if the tx was created in the same session / client instance or if the same CreateClientOptions.EncryptionSeed is used). @@ -95,7 +95,7 @@ public async Task GetTx(string hash, bool tryToDecrypt = true) /// - /// TXSs the query. + /// Returns all transactions that match a query. /// /// The query. /// if set to true the client tries to decrypt the tx data (works only if the tx was created in the same session / client instance or if the same CreateClientOptions.EncryptionSeed is used). @@ -114,7 +114,7 @@ public async Task TxsQuery(string query, bool tryToDecrypt = false) } /// - /// GetTxsEvent fetches txs by event. + /// Returns all transactions that matches the specified events (GetTxsEventRequest.Events). /// /// The request. /// if set to true the client tries to decrypt the tx data (works only if the tx was created in the same session / client instance or if the same CreateClientOptions.EncryptionSeed is used). From 0c3a50e8db9a1a682910a5bee2b82ca361a56167 Mon Sep 17 00:00:00 2001 From: Codemonkey <107538234+0xxCodemonkey@users.noreply.github.com> Date: Sun, 25 Sep 2022 06:02:34 +0200 Subject: [PATCH 24/31] Update README.md --- README.md | 141 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 116 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index cac95f4c..67000152 100644 --- a/README.md +++ b/README.md @@ -155,6 +155,8 @@ var executeContractResult = await secretClient.Tx.Compute.ExecuteContract( txOptions: txOptionsExecute); ``` +You can find **more examples** in the [ready to run example CLI project](https://github.com/0xxCodemonkey/SecretNET/blob/main/examples/SecretNET.Examples/Program.cs). + # API ## Creating / Initializing the wallet When initializing a wallet, you must pass an ```IPrivateKeyStorage``` provider where the private key and mnemonic phrase will be stored (default = MauiSecureStorage). @@ -226,6 +228,8 @@ secretNetworkClient.Wallet = walletFromMnemonic; ### Querier (`secretClient.Query`) The querier can only send queries and get chain information. Access to all query types can be done via ```SecretNetworkClient.Query```. +You can find a full list of all query methods below under ['xx'](#all-queries-eg-accounts-bank-compute-gov-feegrant-etc) or in the [**Full API »**](https://0xxcodemonkey.github.io/SecretNET/html/T-SecretNET.SecretNetworkClient.htm) + #### `secretClient.Query.GetTx(string hash, bool tryToDecrypt = true)` Returns a transaction with a txhash. `hash` is a 64 character upper-case hex string. @@ -294,28 +298,115 @@ var tokenInfoResult = (await snip20Client.Query.GetTokenInfo( Console.WriteLine($"TokenName: {tokenInfoResult.Name}, Symbol: {tokenInfoResult.Symbol}"); ``` +You can find **more examples** in the [ready to run example CLI project](https://github.com/0xxCodemonkey/SecretNET/blob/main/examples/SecretNET.Examples/Program.cs). + ## Transactions On a signer Secret.NET client, `SecretNetworkClient.Tx` is used to broadcast transactions. Every function under `SecretNetworkClient.Tx` can receive an optional `TxOptions`. ### Broadcasting transactions Used to send a complex transactions, which contains a list of messages. The messages are executed in sequence, and the transaction succeeds if all messages succeed. +See code example in simulate. + +### Simulate transactions +Used to simulate a complex transactions, which contains a list of messages, without broadcasting it to the chain. Can be used to get a gas estimation or to see the output without actually committing a transaction on-chain. + +The input should be exactly how you'd use it in `SecretNetworkClient.Tx.Broadcast()`, except that you don't have to pass in `gasLimit`, `gasPriceInFeeDenom` & `feeDenom`. + +Notes: + +- :warning: On mainnet it's recommended to not simulate every transaction as this can burden your node provider. Instead, use this while testing to determine the gas limit for each of your app's transactions, then in production use hard-coded values. +- Gas estimation is known to be a bit off, so you might need to adjust it a bit before broadcasting. ```csharp +var sendToAlice = new Cosmos.Bank.V1Beta1.MsgSend() +{ + FromAddress = wallet.Address, + ToAddress = subaccountWallet.Address +}; +sendToAlice.Amount.Add(new Cosmos.Base.V1Beta1.Coin() { Amount = "1", Denom = "uscrt" }); +var sendToEve = new Cosmos.Bank.V1Beta1.MsgSend() +{ + FromAddress = wallet.Address, + ToAddress = subaccountWallet.Address // use the same address for simplicity +}; +sendToEve.Amount.Add(new Cosmos.Base.V1Beta1.Coin() { Amount = "1", Denom = "uscrt" }); + +var messages = new[] { sendToAlice, sendToEve }; + +var simulate = await secretClient.Tx.Simulate(messages); + +var tx = await secretClient.Tx.Broadcast(messages, new TxOptions +{ + // Adjust gasLimit up by 10% to account for gas estimation error + GasLimit = (int)Math.Ceiling(simulate.GasInfo.GasUsed * 1.1), +}); ``` +### Uploading a smart contract +With you can upload a compiled contract to Secret Network. -### Uploading and initialize smart contract -### Calling a smart contract -### Interacting with an token contract (SNIP20) -### Interacting with an NFT contract (SNIP721) +Input: MsgStoreCodeParams + +```csharp +// https://github.com/0xxCodemonkey/SecretNET/blob/main/resources/mysimplecounter.wasm.gz +byte[] wasmByteCode = File.ReadAllBytes(@"Resources\mysimplecounter.wasm.gz"); + +// MsgStoreCode +var msgStoreCodeCounter = new MsgStoreCode(wasmByteCode, + source: "https://github.com/scrtlabs/secret-template", // Source is a valid absolute HTTPS URI to the contract's source code, optional + builder: "enigmampc/secret-contract-optimizer:latest" // Builder is a valid docker image name with tag, optional + ); +var storeCodeResponse = await secretClient.Tx.Compute.StoreCode(msgStoreCodeCounter, txOptions: txOptionsUpload); +Console.WriteLine("Init Contract with CodeId " + storeCodeResponse.Response.CodeId); +``` +### Instantiate a contract from code id + +```csharp +var codeId = storeCodeResponse.Response.CodeId; // see above +var codeHash = await secretClient.Query.Compute.GetCodeHashByCodeId(codeId); // get codeHash (optional) +var msgInitContract = new MsgInstantiateContract( + codeId: codeId, + label: $"MySimpleCouter {codeId}", + initMsg: new { count = 100 }, + codeHash: codeHash // optional but way faster + ); + +var initContractResponse = await secretClient.Tx.Compute.InstantiateContract(msgInitContract, txOptions: txOptionsUpload); +logSecretTx("InstantiateContract", initContractResponse); + +const contractAddress = initContractResponse.Response.Address); + +``` + +### Calling a smart contract +Execute a function on a contract + +```csharp +var msgExecuteContract = new MsgExecuteContract( + contractAddress: contractAddress, + msg: executeMsg, + codeHash: contractCodeHash, + sender: null, // optional => set to the wallet address + sentFunds: null // optional + ); + +var tx = await secretClient.Tx.Compute.ExecuteContract(msgExecuteContract, new TxOptions +{ + // Adjust gasLimit to you got from a simulate tx + GasLimit = 150000, +}); + +``` +You can find more examples in the [ready to run example CLI project](https://github.com/0xxCodemonkey/SecretNET/blob/main/examples/SecretNET.Examples/Program.cs). +# Overview of all query and transaction methods -#### All queries (eg. accounts, bank, compute, gov, feegrant, etc.) +## All queries (eg. accounts, bank, compute, gov, feegrant, etc.) - secretClient.Query.Auth - secretClient.Query.Authz - secretClient.Query.Bank @@ -338,16 +429,16 @@ Used to send a complex transactions, which contains a list of messages. The mess See all details in the [**Full API »**](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.Queries.htm) -##### [secretClient.Query.Auth](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.AuthQueryClient.htm) +### [secretClient.Query.Auth](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.AuthQueryClient.htm) - `Account(string address)` => Returns account details based on address. - `Accounts()` => Returns all existing accounts on the blockchain. -##### [secretClient.Query.Authz](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.AuthzQueryClient.htm) +### [secretClient.Query.Authz](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.AuthzQueryClient.htm) - `GranteeGrants(QueryGranteeGrantsRequest request)` => GranteeGrants returns a list of `GrantAuthorization` by grantee. Since: cosmos-sdk 0.45.2. - `GranterGrants(QueryGranterGrantsRequest request)` => GranterGrants returns list of `GrantAuthorization`, granted by granter. Since: cosmos-sdk 0.45.2. - `Grants(QueryGrantsRequest request)` => Returns list of `Authorization`, granted to the grantee by the granter. -##### [secretClient.Query.Bank](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.BankQueryClient.htm) +### [secretClient.Query.Bank](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.BankQueryClient.htm) - `Balance(QueryBalanceRequest request)` / `Balance(string address, string denom)` => Balance queries the balance of **a single** coin for a single account. - `Balance(QueryAllBalancesRequest request)` => AllBalances queries the balance of **all coins** for a single account. - `DenomMetadata(QueryDenomMetadataRequest request)` => DenomsMetadata queries the client metadata of **a given coin** denomination. @@ -357,7 +448,7 @@ See all details in the [**Full API »**](https://0xxcodemonkey.github.io/SecretN - `SupplyOf(QuerySupplyOfRequest request)` => SupplyOf queries the supply of a single coin. - `TotalSupply(QueryTotalSupplyRequest request)` => TotalSupply queries the total supply of all coins. -##### [secretClient.Query.Compute](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.ComputeQueryClient.htm) +### [secretClient.Query.Compute](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.ComputeQueryClient.htm) - `Code(ulong codeId, Metadata metadata)` => Get WASM bytecode and metadata for a code id. - `Codes(Metadata metadata)` => Query all codes on chain. - `ContractInfo(string contractAddress, Metadata metadata)` => Get metadata of a Secret Contract. @@ -366,7 +457,7 @@ See all details in the [**Full API »**](https://0xxcodemonkey.github.io/SecretN - `GetCodeHashByCodeId(ulong codeId, Metadata metadata)` => Get the codeHash from a code id. - `QueryContract(string contractAddress, Object queryMsg, string codeHash, Metadata metadata)` => Query a Secret Contract and cast the response as `R`. (see above) -##### [secretClient.Query.Distribution](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.DistributionQueryClient.htm) +### [secretClient.Query.Distribution](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.DistributionQueryClient.htm) - `Balance(QueryParamsRequest request)` => CommunityPool queries the community pool coins. - `CommunityPool(QueryCommunityPoolRequest request)` => Query all codes on chain. - `DelegationRewards(QueryDelegationRewardsRequest request)` => DelegationRewards queries the total rewards accrued by a delegation. @@ -378,15 +469,15 @@ See all details in the [**Full API »**](https://0xxcodemonkey.github.io/SecretN - `ValidatorOutstandingRewards(QueryValidatorCommissionRequest request)` => ValidatorOutstandingRewards queries rewards of a validator address. - `ValidatorSlashes(QueryValidatorCommissionRequest request)` => ValidatorSlashes queries slash events of a validator. -##### [secretClient.Query.Evidence](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.EvidenceQueryClient.htm) +### [secretClient.Query.Evidence](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.EvidenceQueryClient.htm) - `AllEvidence(QueryAllEvidenceRequest request)` => AllEvidence queries all evidence. - `FoundationTax(QueryEvidenceRequest request)` => Evidence queries evidence based on evidence hash. -##### [secretClient.Query.Feegrant](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.FeegrantQueryClient.htm) +### [secretClient.Query.Feegrant](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.FeegrantQueryClient.htm) - `Allowance(QueryAllowanceRequest request)` => Allowance returns fee granted to the grantee by the granter. - `Allowances(QueryAllowancesRequest request)` => Allowances returns all the grants for address. -#### [secretClient.Query.Gov](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.GovQueryClient.htm) +### [secretClient.Query.Gov](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.GovQueryClient.htm) - `Deposit(QueryDepositRequest request)` => Deposit queries single deposit information based proposalID, depositAddr. - `Deposits(QueryDepositsRequest request)` => Deposits queries all deposits of a single proposal. - `Params(QueryParamsRequest request)` => Params queries all parameters of the gov module. @@ -396,7 +487,7 @@ See all details in the [**Full API »**](https://0xxcodemonkey.github.io/SecretN - `Vote(QueryVoteRequest request)` => Vote queries voted information based on proposalID, voterAddr. - `Votes(QueryVotesRequest request)` => Votes queries votes of a given proposal. -#### [secretClient.Query.IbcChannel](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.IbcChannelQueryClient.htm) +### [secretClient.Query.IbcChannel](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.IbcChannelQueryClient.htm) - `Channel(QueryChannelRequest request)` => Channel queries an IBC Channel. - `Channels(QueryChannelsRequest request)` => Channels queries all the IBC channels of a chain. - `ChannelClientState(QueryChannelClientStateRequest request)` => ChannelClientState queries for the client state for the channel associated with the provided channel identifiers. @@ -411,7 +502,7 @@ See all details in the [**Full API »**](https://0xxcodemonkey.github.io/SecretN - `UnreceivedAcks(QueryUnreceivedAcksRequest request)` => UnreceivedAcks returns all the unreceived IBC acknowledgements associated with a channel and sequences. - `UnreceivedPackets(QueryUnreceivedPacketsRequest request)` => UnreceivedPackets returns all the unreceived IBC packets associated with a channel and sequences. -#### [secretClient.Query.IbcClient](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.IbcClientQueryClient.htm) +### [secretClient.Query.IbcClient](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.IbcClientQueryClient.htm) - `ClientParams(QueryClientParamsRequest request)` => ClientParams queries all parameters of the ibc client. - `ClientState(QueryClientStateRequest request)` => ClientState queries an IBC light client. - `ClientStates(QueryClientStatesRequest request)` => ClientStates queries all the IBC light clients of a chain. @@ -421,38 +512,38 @@ See all details in the [**Full API »**](https://0xxcodemonkey.github.io/SecretN - `UpgradedClientState(QueryUpgradedClientStateRequest request)` => UpgradedClientState queries an Upgraded IBC light client. - `UpgradedConsensusState(QueryUpgradedConsensusStateRequest request)` => UpgradedConsensusState queries an Upgraded IBC consensus state. -#### [secretClient.Query.IbcConnection](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.IbcConnectionQueryClient.htm) +### [secretClient.Query.IbcConnection](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.IbcConnectionQueryClient.htm) - `ClientConnections(QueryClientConnectionsRequest request)` => ClientConnections queries the connection paths associated with a client state. - `Connection(QueryConnectionRequest request)` => Connection queries an IBC connection end. - `Connections(QueryConnectionsRequest request)` => Connections queries all the IBC connections of a chain. - `ConnectionClientState(QueryConnectionClientStateRequest request)` => ConnectionClientState queries the client state associated with the connection. - `ConnectionConsensusState(QueryConnectionConsensusStateRequest request)` => ConnectionConsensusState queries the consensus state associated with the connection. -#### [secretClient.Query.IbcTransfer](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.IbcTransferQueryClient.htm) +### [secretClient.Query.IbcTransfer](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.IbcTransferQueryClient.htm) - `DenomHash(QueryDenomHashRequest request)` => DenomHash queries a denomination hash information. - `DenomTrace(QueryDenomTraceRequest request)` => DenomTrace queries a denomination trace information. - `DenomTraces(QueryDenomTracesRequest request)` => DenomTraces queries all denomination traces. - `Params(QueryParamsRequest request)` => Params queries all parameters of the ibc-transfer module. -#### [secretClient.Query.Mint](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.MintQueryClient.htm) +### [secretClient.Query.Mint](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.MintQueryClient.htm) - `AnnualProvisions(QueryAnnualProvisionsRequest request)` => AnnualProvisions current minting annual provisions value. - `Inflation(QueryInflationRequest request)` => Inflation returns the current minting inflation value. - `Params(QueryParamsRequest request)` => Params returns the total set of minting parameters. -#### [secretClient.Query.Params](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.ParamsQueryClient.htm) +### [secretClient.Query.Params](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.ParamsQueryClient.htm) - `Params(QueryParamsRequest request)` => Params queries a specific parameter of a module, given its subspace and key. -#### [secretClient.Query.Registration](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.RegistrationQueryClient.htm) +### [secretClient.Query.Registration](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.RegistrationQueryClient.htm) - `EncryptedSeed(QueryEncryptedSeedRequest request)` => Encrypteds the seed. - `RegistrationKey()` => Returns the key used for registration. - `TxKey()` => Returns the key used for transactions. -#### [secretClient.Query.Slashing](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.SlashingQueryClient.htm) +### [secretClient.Query.Slashing](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.SlashingQueryClient.htm) - `Params(QueryParamsRequest request)` => Params queries the parameters of slashing module. - `SigningInfo(QuerySigningInfoRequest request)` => SigningInfo queries the signing info of given cons address. - `SigningInfos(QuerySigningInfosRequest request)` => SigningInfos queries signing info of all validators. -#### [secretClient.Query.Staking](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.StakingQueryClient.htm) +### [secretClient.Query.Staking](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.StakingQueryClient.htm) - `Delegation(QueryDelegationRequest request)` => Delegation queries delegate info for given validator delegator pair. - `DelegatorDelegations(QueryDelegatorDelegationsRequest request)` => DelegatorDelegations queries all delegations of a given delegator address. - `DelegatorValidator(QueryDelegatorValidatorRequest request)` => DelegatorValidator queries validator info for given delegator validator pair. @@ -468,7 +559,7 @@ See all details in the [**Full API »**](https://0xxcodemonkey.github.io/SecretN - `ValidatorDelegations(QueryValidatorDelegationsRequest request)` => ValidatorDelegations queries delegate info for given validator. - `ValidatorUnbondingDelegations(QueryValidatorUnbondingDelegationsRequest request)` => ValidatorUnbondingDelegations queries unbonding delegations of a validator. -#### [secretClient.Query.Tendermint](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.TendermintQueryClient.htm) +### [secretClient.Query.Tendermint](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.TendermintQueryClient.htm) - `GetBlockByHeight(GetBlockByHeightRequest request)` => GetBlockByHeight queries block for given height. - `GetLatestBlock(GetLatestBlockRequest request)` => GetLatestBlock returns the latest block. - `GetLatestValidatorSet(GetLatestValidatorSetRequest request)` => GetLatestValidatorSet queries latest validator-set. @@ -476,12 +567,12 @@ See all details in the [**Full API »**](https://0xxcodemonkey.github.io/SecretN - `GetSyncing(GetSyncingRequest request)` => GetSyncing queries node syncing. - `GetValidatorSetByHeight(GetValidatorSetByHeightRequest request)` => GetValidatorSetByHeight queries validator-set at a given height. -#### [secretClient.Query.Upgrade](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.UpgradeQueryClient.htm) +### [secretClient.Query.Upgrade](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.UpgradeQueryClient.htm) - `AppliedPlan(QueryAppliedPlanRequest request)` => AppliedPlan queries a previously applied upgrade plan by its name. - `CurrentPlan(QueryCurrentPlanRequest request)` => CurrentPlan queries the current upgrade plan. - `ModuleVersions(QueryModuleVersionsRequest request)` => ModuleVersions queries the list of module versions from state. Since: cosmos-sdk 0.43. -### All transactions (eg. ) +## All transactions (eg. ) From 0fdb254622da29cc6a1c947c1b217aefbc450034 Mon Sep 17 00:00:00 2001 From: Codemonkey <107538234+0xxCodemonkey@users.noreply.github.com> Date: Sun, 25 Sep 2022 06:09:09 +0200 Subject: [PATCH 25/31] Update README.md --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index 67000152..ea1ce38a 100644 --- a/README.md +++ b/README.md @@ -342,6 +342,20 @@ var tx = await secretClient.Tx.Broadcast(messages, new TxOptions GasLimit = (int)Math.Ceiling(simulate.GasInfo.GasUsed * 1.1), }); ``` + +### Sending SCRT + +```csharp +var sendResponse = await secretClient.Tx.Bank.Send( + toAddress: alice, + amount: 1000000, + denom: null, // default "uscrt" + new TxOptions{ GasLimit = 20000,} + ); + +var success = sendResponse.Code == 0; +``` + ### Uploading a smart contract With you can upload a compiled contract to Secret Network. From ad4f8622d2ebc37b5e88ea93a59d4da0986fce85 Mon Sep 17 00:00:00 2001 From: Codemonkey <107538234+0xxCodemonkey@users.noreply.github.com> Date: Sun, 25 Sep 2022 06:27:52 +0200 Subject: [PATCH 26/31] Update README.md --- README.md | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index ea1ce38a..0ad293bd 100644 --- a/README.md +++ b/README.md @@ -228,7 +228,7 @@ secretNetworkClient.Wallet = walletFromMnemonic; ### Querier (`secretClient.Query`) The querier can only send queries and get chain information. Access to all query types can be done via ```SecretNetworkClient.Query```. -You can find a full list of all query methods below under ['xx'](#all-queries-eg-accounts-bank-compute-gov-feegrant-etc) or in the [**Full API »**](https://0xxcodemonkey.github.io/SecretNET/html/T-SecretNET.SecretNetworkClient.htm) +You can find a full list of all query methods below under ['All query methods'](#all-query-methods) or in the [**Full API »**](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.Queries.htm) #### `secretClient.Query.GetTx(string hash, bool tryToDecrypt = true)` Returns a transaction with a txhash. `hash` is a 64 character upper-case hex string. @@ -254,7 +254,7 @@ To create a query for txs where AddrA transferred funds: `transfer.sender = 'Add See `txsQuery` under https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.Queries.htm -### Get SCRT Balance +#### Get SCRT Balance ```csharp var response = await secretClient.Query.Bank.Balance("secret1ap26qrlp8mcq2pg6r47w43l0y8zkqm8a450s03"); @@ -303,6 +303,8 @@ You can find **more examples** in the [ready to run example CLI project](https:/ ## Transactions On a signer Secret.NET client, `SecretNetworkClient.Tx` is used to broadcast transactions. Every function under `SecretNetworkClient.Tx` can receive an optional `TxOptions`. +You can find a full list of all transaction methods below under ['All transaction methods'](#all-transaction-methods) or in the [**Full API »**]([https://0xxcodemonkey.github.io/SecretNET/html/T-SecretNET.SecretNetworkClient.htm](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Tx.TxClient.htm)) + ### Broadcasting transactions Used to send a complex transactions, which contains a list of messages. The messages are executed in sequence, and the transaction succeeds if all messages succeed. See code example in simulate. @@ -420,7 +422,7 @@ You can find more examples in the [ready to run example CLI project](https://git # Overview of all query and transaction methods -## All queries (eg. accounts, bank, compute, gov, feegrant, etc.) +## All query methods - secretClient.Query.Auth - secretClient.Query.Authz - secretClient.Query.Bank @@ -443,6 +445,11 @@ You can find more examples in the [ready to run example CLI project](https://git See all details in the [**Full API »**](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.Queries.htm) +### [secretClient.Query](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.Queries.htm) +- `GetTx(string hash, bool tryToDecrypt = true)` => Returns a transaction with a txhash. `hash` is a 64 character upper-case hex string. (see [above](#secretclientquerygettxstring-hash-bool-trytodecrypt--true)) +- `TxsQuery(string query, bool tryToDecrypt = false)` => Returns all transactions that match a query. (see [above](#secretclientquerytxsquerystring-query-bool-trytodecrypt--false)) +- `GetTxsEvent(GetTxsEventRequest request, bool tryToDecrypt = false)` => Returns all transactions that matches the specified events (`GetTxsEventRequest.Events`). + ### [secretClient.Query.Auth](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Query.AuthQueryClient.htm) - `Account(string address)` => Returns account details based on address. - `Accounts()` => Returns all existing accounts on the blockchain. @@ -586,7 +593,5 @@ See all details in the [**Full API »**](https://0xxcodemonkey.github.io/SecretN - `CurrentPlan(QueryCurrentPlanRequest request)` => CurrentPlan queries the current upgrade plan. - `ModuleVersions(QueryModuleVersionsRequest request)` => ModuleVersions queries the list of module versions from state. Since: cosmos-sdk 0.43. - - -## All transactions (eg. ) - +## All transaction methods +- x From 573c1774f93d883d67d5929a124f569a07cbe75a Mon Sep 17 00:00:00 2001 From: Codemonkey <107538234+0xxCodemonkey@users.noreply.github.com> Date: Sun, 25 Sep 2022 06:41:10 +0200 Subject: [PATCH 27/31] Update README.md --- README.md | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0ad293bd..81503b3a 100644 --- a/README.md +++ b/README.md @@ -594,4 +594,55 @@ See all details in the [**Full API »**](https://0xxcodemonkey.github.io/SecretN - `ModuleVersions(QueryModuleVersionsRequest request)` => ModuleVersions queries the list of module versions from state. Since: cosmos-sdk 0.43. ## All transaction methods -- x +- secretClient.Tx.Authz +- secretClient.Tx.Bank +- secretClient.Tx.Compute +- secretClient.Tx.Crisis +- secretClient.Tx.Distribution +- secretClient.Tx.Evidence +- secretClient.Tx.Feegrant +- secretClient.Tx.Gov +- secretClient.Tx.IbcChannel +- secretClient.Tx.IbcClient +- secretClient.Tx.IbcConnection +- secretClient.Tx.IbcTransfer +- secretClient.Tx.Slashing +- secretClient.Tx.Staking +- secretClient.Tx.Vesting + +See all details in the [**Full API »**](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Tx.TxClient.htm) + +### [secretClient.Tx](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Tx.TxClient.htm) +- `Simulate(IMessage message, TxOptions txOptions = null)` => Used to simulate a complex transactions, which contains a list of messages, without broadcasting it to the chain. (has several overloads). +- `Broadcast(IMessage message, TxOptions txOptions = null)` => Used to send a complex transactions, which contains a list of messages. The messages are executed in sequence, and the transaction succeeds if all messages succeed. (has several overloads). +- `Broadcast(IMessage message, TxOptions txOptions = null)` => Like Broadcast but tries to convert the first message result to T. (has several overloads). + +### [secretClient.Tx.Authz](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Tx.AuthzTx.htm) + +### [secretClient.Tx.Bank](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Tx.BankTx.htm) + +### [secretClient.Tx.Compute](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Tx.ComputeTx.htm) + +### [secretClient.Tx.Crisis](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Tx.CrisisTx.htm) + +### [secretClient.Tx.Distribution](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Tx.DistributionTx.htm) + +### [secretClient.Tx.Evidence](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Tx.EvidenceTx.htm) + +### [secretClient.Tx.Feegrant](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Tx.FeegrantTx.htm) + +### [secretClient.Tx.Gov](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Tx.GovTx.htm) + +### [secretClient.Tx.IbcChannel](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Tx.IbcChannelTx.htm) + +### [secretClient.Tx.IbcClient](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Tx.IbcClientTx.htm) + +### [secretClient.Tx.IbcConnection](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Tx.IbcConnectionTx.htm) + +### [secretClient.Tx.IbcTransfer](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Tx.IbcTransferTx.htm) + +### [secretClient.Tx.Slashing](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Tx.SlashingTx.htm) + +### [secretClient.Tx.Staking](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Tx.StakingTx.htm) + +### [secretClient.Tx.Vesting](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Tx.VestingTx.htm) From c10cafa38deb5a629345b1c248031eb0c31f25e2 Mon Sep 17 00:00:00 2001 From: Codemonkey <107538234+0xxCodemonkey@users.noreply.github.com> Date: Sun, 25 Sep 2022 06:46:13 +0200 Subject: [PATCH 28/31] Update README.md --- README.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 81503b3a..dad5447b 100644 --- a/README.md +++ b/README.md @@ -45,12 +45,10 @@ You can find the **full API-documentation** here => [https://0xxcodemonkey.githu - [Attaching the wallet to the SecretNetworkClient (required for signing transactions)](#attaching-the-wallet-to-the-secretnetworkclient-required-for-signing-transactions) - [SecretNetworkClient](#secretnetworkclient) - [Queries](#querier-secretclientquery) - - [All queries (eg. accounts, bank, compute, gov, feegrant, etc.)](#all-queries-eg-accounts-bank-compute-gov-feegrant-etc) - - [Transactions](#transactions) - - [Uploading and initialize Smart Contract](#uploading-and-initialize-smart-contract) - - [Interacting with an Token Contract (SNIP20)](#interacting-with-an-token-contract-snip20) - - [Interacting with an NFT Contract (SNIP721)](#interacting-with-an-nft-contract-snip721) - - [All transactions (eg. )](#all-transactions-eg-) + - [Transactions](#transactions) + - [Overview of all query and transaction methods](#overview-of-all-query-and-transaction-methods) + - [All query methods](#all-query-methods) + - [All transaction methods](#all-transaction-methods) # General information The rough structure of the Secret.NET client, from the user's perspective, is divided into the following areas: From 9cd5f3f015b175f1aef69c1d4ed2dbf22aecbd7f Mon Sep 17 00:00:00 2001 From: Codemonkey <107538234+0xxCodemonkey@users.noreply.github.com> Date: Sun, 25 Sep 2022 07:12:32 +0200 Subject: [PATCH 29/31] Update README.md --- README.md | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index dad5447b..701acf82 100644 --- a/README.md +++ b/README.md @@ -616,31 +616,81 @@ See all details in the [**Full API »**](https://0xxcodemonkey.github.io/SecretN - `Broadcast(IMessage message, TxOptions txOptions = null)` => Like Broadcast but tries to convert the first message result to T. (has several overloads). ### [secretClient.Tx.Authz](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Tx.AuthzTx.htm) +- `Exec(MsgExec msg, TxOptions txOptions = null)` => Exec attempts to execute the provided messages using authorizations granted to the grantee. Each message should have only one signer corresponding to the granter of the authorization. +- `Grant(MsgExec msg, TxOptions txOptions = null)` => Grant is a request type for Grant method. It declares authorization to the grantee on behalf of the granter with the provided expiration time. +- `Revoke(MsgRevoke msg, TxOptions txOptions = null)` => Revoke revokes any authorization with the provided sdk.Msg type on the granter's account with that has been granted to the grantee. ### [secretClient.Tx.Bank](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Tx.BankTx.htm) - +- `Send(string toAddress, int amount, string denom, TxOptions txOptions = null)` => Sends SCRT to the specified to address. +- `Send(MsgSend msg, TxOptions txOptions = null)` => MsgSend represents a message to send coins from one account to another. +- `MultiSend(MsgMultiSend msg, TxOptions txOptions = null)` => MsgMultiSend represents an arbitrary multi-in, multi-out send message. +- ### [secretClient.Tx.Compute](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Tx.ComputeTx.htm) +- `ExecuteContract(MsgExecuteContract msg, TxOptions txOptions = null)` => Execute a function on a contract. (see also [above](#calling-a-smart-contract)) +- `ExecuteContract(MsgExecuteContract msg, TxOptions txOptions = null)` => Execute a function on a contract and tries to convert the response to T. +- `InstantiateContract(MsgInstantiateContract msg, TxOptions txOptions = null)` => Instantiate a contract from code id. (see also [above](#instantiate-a-contract-from-code-id)) +- `StoreCode(MsgStoreCode msg, TxOptions txOptions = null)` => Upload a compiled contract to Secret Network. (see also [above](#uploading-a-smart-contract)) ### [secretClient.Tx.Crisis](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Tx.CrisisTx.htm) +- `VerifyInvariant(MsgVerifyInvariant msg, TxOptions txOptions = null)` => MsgVerifyInvariant represents a message to verify a particular invariance. ### [secretClient.Tx.Distribution](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Tx.DistributionTx.htm) +- `FundCommunityPool(MsgFundCommunityPool msg, TxOptions txOptions = null)` => MsgFundCommunityPool allows an account to directly fund the community pool. +- `SetWithdrawAddress(MsgSetWithdrawAddress msg, TxOptions txOptions = null)` => MsgSetWithdrawAddress sets the withdraw address for a delegator (or validator self-delegation). +- `WithdrawDelegatorReward(MsgWithdrawDelegatorReward msg, TxOptions txOptions = null)` => MsgWithdrawDelegatorReward represents delegation withdrawal to a delegator from a single validator. +- `WithdrawValidatorCommission(MsgExec msg, TxOptions txOptions = null)` => MsgWithdrawValidatorCommission withdraws the full commission to the validator address. ### [secretClient.Tx.Evidence](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Tx.EvidenceTx.htm) +- `SubmitEvidence(MsgSubmitEvidence msg, TxOptions txOptions = null)` => MsgSubmitEvidence represents a message that supports submitting arbitrary Evidence of misbehavior such as equivocation or counterfactual signing. ### [secretClient.Tx.Feegrant](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Tx.FeegrantTx.htm) +- `GrantAllowance(MsgGrantAllowance msg, TxOptions txOptions = null)` => MsgGrantAllowance adds permission for Grantee to spend up to Allowance of fees from the account of Granter. +- `RevokeAllowance(MsgRevokeAllowance msg, TxOptions txOptions = null)` => MsgRevokeAllowance removes any existing Allowance from Granter to Grantee. ### [secretClient.Tx.Gov](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Tx.GovTx.htm) +- `Deposit(MsgDeposit msg, TxOptions txOptions = null)` => MsgDeposit defines a message to submit a deposit to an existing proposal. +- `SubmitProposal(MsgSubmitProposal msg, TxOptions txOptions = null)` => MsgSubmitProposal defines an sdk.Msg type that supports submitting arbitrary proposal Content. +- `Vote(MsgVote msg, TxOptions txOptions = null)` => MsgVote defines a message to cast a vote. +- `VoteWeighted(MsgVoteWeighted msg, TxOptions txOptions = null)` => MsgVoteWeighted defines a message to cast a vote, with an option to split the vote. ### [secretClient.Tx.IbcChannel](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Tx.IbcChannelTx.htm) +- `Acknowledgement(MsgAcknowledgement msg, TxOptions txOptions = null)` => +- `ChannelCloseConfirm(MsgChannelCloseConfirm msg, TxOptions txOptions = null)` => +- `ChannelCloseInit(MsgChannelCloseInit msg, TxOptions txOptions = null)` => +- `ChannelOpenAck(MsgChannelOpenAck msg, TxOptions txOptions = null)` => +- `ChannelOpenConfirm(MsgChannelOpenConfirm msg, TxOptions txOptions = null)` => +- `ChannelOpenInit(MsgChannelOpenInit msg, TxOptions txOptions = null)` => +- `ChannelOpenTry(MsgChannelOpenTry msg, TxOptions txOptions = null)` => +- `RecvPacket(MsgRecvPacket msg, TxOptions txOptions = null)` => +- `Timeout(MsgTimeout msg, TxOptions txOptions = null)` => +- `TimeoutOnClose(MsgTimeoutOnClose msg, TxOptions txOptions = null)` => ### [secretClient.Tx.IbcClient](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Tx.IbcClientTx.htm) +- `CreateClient(MsgCreateClient msg, TxOptions txOptions = null)` => +- `SubmitMisbehaviour(MsgSubmitMisbehaviour msg, TxOptions txOptions = null)` => +- `UpdateClient(MsgUpdateClient msg, TxOptions txOptions = null)` => +- `UpgradeClient(MsgUpgradeClient msg, TxOptions txOptions = null)` => ### [secretClient.Tx.IbcConnection](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Tx.IbcConnectionTx.htm) +- `ConnectionOpenAck(MsgConnectionOpenAck msg, TxOptions txOptions = null)` => +- `ConnectionOpenConfirm(MsgConnectionOpenConfirm msg, TxOptions txOptions = null)` => +- `ConnectionOpenInit(MsgConnectionOpenInit msg, TxOptions txOptions = null)` => +- `ConnectionOpenTry(MsgConnectionOpenTry msg, TxOptions txOptions = null)` => ### [secretClient.Tx.IbcTransfer](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Tx.IbcTransferTx.htm) +- `Transfer(MsgTransfer msg, TxOptions txOptions = null)` => MsgTransfer defines a msg to transfer fungible tokens (i.e Coins) between ICS20 enabled chains. See ICS Spec here: https://github.com/cosmos/ics/tree/master/spec/ics-020-fungible-token-transfer#data-structures ### [secretClient.Tx.Slashing](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Tx.SlashingTx.htm) +- `Unjail(MsgUnjail msg, TxOptions txOptions = null)` => MsgUnjail defines a message to release a validator from jail. ### [secretClient.Tx.Staking](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Tx.StakingTx.htm) +- `BeginRedelegate(MsgBeginRedelegate msg, TxOptions txOptions = null)` => MsgBeginRedelegate defines an SDK message for performing a redelegation of coins from a delegator and source validator to a destination validator. +- `CreateValidator(MsgCreateValidator msg, TxOptions txOptions = null)` => MsgCreateValidator defines an SDK message for creating a new validator. +- `Delegate(MsgDelegate msg, TxOptions txOptions = null)` => MsgDelegate defines an SDK message for performing a delegation of coins from a delegator to a validator. +- `EditValidator(MsgEditValidator msg, TxOptions txOptions = null)` => MsgEditValidator defines an SDK message for editing an existing validator. +- `Undelegate(MsgUndelegate msg, TxOptions txOptions = null)` => MsgUndelegate defines an SDK message for performing an undelegation from a delegate and a validator ### [secretClient.Tx.Vesting](https://0xxcodemonkey.github.io/SecretNET/html/AllMembers.T-SecretNET.Tx.VestingTx.htm) +- `CreateVestingAccount(MsgCreateVestingAccount msg, TxOptions txOptions = null)` => MsgCreateVestingAccount defines a message that enables creating a vesting account. + + From 5b5ceb1977a06ff76bc37e9d0b51baed5050c45c Mon Sep 17 00:00:00 2001 From: Codemonkey <107538234+0xxCodemonkey@users.noreply.github.com> Date: Sun, 25 Sep 2022 07:13:08 +0200 Subject: [PATCH 30/31] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 701acf82..209ea2ef 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Secret.NET Core Library -**Secret.NET** (port of the [secret.js](https://github.com/scrtlabs/secret.js) Client) is a .NET Client to interact with the [Secret Network blockchain](https://scrt.network/) (L1 / Cosmos based), the first privacy smart contract blockchain that processes and stores data on-chain in encrypted form (SGX). +**Secret.NET** (port of the [secret.js](https://github.com/scrtlabs/secret.js) Client) is a .NET Client to interact with the [Secret Network blockchain](https://scrt.network/) (L1 / Cosmos based), **the first privacy smart contract blockchain** that processes and stores data on-chain in encrypted form (SGX). This allows [unique use cases](https://docs.scrt.network/secret-network-documentation/secret-network-overview/use-cases) like **Secret NFTs where you can store public and private data** e.g., encryption keys, passwords or other secrets. From c3e4ec920bc7541b76b05c4bbc3b5f8ef09c4f5f Mon Sep 17 00:00:00 2001 From: 0xxCodemonkey Date: Sun, 25 Sep 2022 07:14:28 +0200 Subject: [PATCH 31/31] Docs and some changes --- examples/SecretNET.Examples/Program.cs | 69 ++++++++++++++----- resources/mysimplecounter.wasm.gz | Bin 0 -> 40961 bytes resources/snip20_reference_impl.wasm.gz | Bin 0 -> 147998 bytes resources/snip721_722_reference_impl.wasm.gz | Bin 0 -> 229903 bytes src/Api/SecretUtils.cs | 8 +-- src/Common/CreateClientOptions.cs | 2 +- src/Common/SecretEncryptionUtil.cs | 2 +- src/Crypto/DataEncoders/ASCIIEncoder.cs | 2 +- src/Query/ComputeQueryClient.cs | 12 ++-- src/Query/Queries.cs | 10 +-- src/SecretNetworkClient.cs | 7 +- src/Tx/Compute.Msg.cs | 6 +- src/Tx/ComputeTx.cs | 6 +- src/Tx/MsgDecoderRegistry.cs | 2 +- src/Tx/TxClient.cs | 68 +++++++++++++++--- 15 files changed, 142 insertions(+), 52 deletions(-) create mode 100644 resources/mysimplecounter.wasm.gz create mode 100644 resources/snip20_reference_impl.wasm.gz create mode 100644 resources/snip721_722_reference_impl.wasm.gz diff --git a/examples/SecretNET.Examples/Program.cs b/examples/SecretNET.Examples/Program.cs index ad606077..6dd781d8 100644 --- a/examples/SecretNET.Examples/Program.cs +++ b/examples/SecretNET.Examples/Program.cs @@ -5,6 +5,7 @@ global using SecretNET.Tx; global using SecretNET.Common; global using SecretNET.Common.Storage; +using System.Reflection.Metadata; // See https://aka.ms/new-console-template for more information @@ -40,7 +41,7 @@ if (tx != null && (tx.Code > 0 || (tx.Exceptions?.Any()).GetValueOrDefault())) { Console.ForegroundColor = ConsoleColor.Red; - if (tx.Code > 0 && !string.IsNullOrEmpty(tx.Codespace)) + if (tx.Code > 0 && !string.IsNullOrWhiteSpace(tx.Codespace)) { Console.WriteLine($"\r\n!!!!!!!!!!!! Something went wrong => Code: {tx.Code}; Codespace: {tx.Codespace} !!!!!!!!!!!!"); } @@ -126,7 +127,7 @@ var subaccountWallet = await wallet.GetSubaccount(1); Console.WriteLine($"\r\nSubaccount.Address: {subaccountWallet.Address}"); -var sendResponse = await secretClient.Tx.Bank.Send(subaccountWallet.Address, 1000000); +var sendResponse = await secretClient.Tx.Bank.Send(toAddress: subaccountWallet.Address, amount: 1000000, denom: null); Console.WriteLine($"BroadcastResponse: {(sendResponse.Code == 0 ? "Success" : "Error (see log)")}"); var r1 = await secretClient.Query.Bank.Balance(subaccountWallet.Address); @@ -136,6 +137,40 @@ #endregion +#region *** Simulate and broadcast a complex transaction *** + +var sendToAlice = new Cosmos.Bank.V1Beta1.MsgSend() +{ + FromAddress = wallet.Address, + ToAddress = subaccountWallet.Address +}; +sendToAlice.Amount.Add(new Cosmos.Base.V1Beta1.Coin() { Amount = "1", Denom = "uscrt" }); + +var sendToEve = new Cosmos.Bank.V1Beta1.MsgSend() +{ + FromAddress = wallet.Address, + ToAddress = subaccountWallet.Address // use the same address for simplicity +}; +sendToEve.Amount.Add(new Cosmos.Base.V1Beta1.Coin() { Amount = "1", Denom = "uscrt" }); + +var messages = new[] { sendToAlice, sendToEve }; + +var simulate = await secretClient.Tx.Simulate(messages); + +Console.WriteLine($"Simulate => GasUsed {simulate.GasInfo.GasUsed} uscrt"); + +var tx = await secretClient.Tx.Broadcast(messages, new TxOptions +{ + // Adjust gasLimit up by 10% to account for gas estimation error + GasLimit = (int)Math.Ceiling(simulate.GasInfo.GasUsed * 1.1), +}); + +logSecretTx("Broadcast result", tx); + +//Console.ReadLine(); + +#endregion + #region *** Auth *** // *** Get Account *** @@ -156,7 +191,7 @@ writeHeadline("Get my Codes with source"); var codesResponse = await secretClient.Query.Compute.Codes(); -var withSource = codesResponse.Where(c => !String.IsNullOrEmpty(c.Source) && c.CreatorAddress == wallet.Address).ToList(); +var withSource = codesResponse.Where(c => !string.IsNullOrWhiteSpace(c.Source) && c.CreatorAddress == wallet.Address).ToList(); Console.WriteLine($"My Codes with source (Count: {withSource.Count} ):\r\n" + JsonConvert.SerializeObject(withSource, Formatting.Indented) + "\r\n"); //Console.ReadLine(); @@ -172,7 +207,7 @@ // *** Upload Contract *** // https://secretjs.scrt.network/#secretjstxcomputestorecode -byte[] wasmByteCode = File.ReadAllBytes(@"C:\_GitHub\mhorn69\secretNET\Resources\SmartContracts\mysimplecounter.wasm.gz"); +byte[] wasmByteCode = File.ReadAllBytes(@"..\..\..\..\..\Resources\mysimplecounter.wasm.gz"); // MsgStoreCode var msgStoreCodeCounter = new MsgStoreCode(wasmByteCode, @@ -191,20 +226,22 @@ if (storeCodeResponse.Response.CodeId > 0) { var codeId = storeCodeResponse.Response.CodeId; - var initMsg = new { count = 100 }; - Console.WriteLine("InstantiateContract:\r\n" + JsonConvert.SerializeObject(initMsg, Formatting.Indented) + "\r\n"); + contractCodeHash = await secretClient.Query.Compute.GetCodeHashByCodeId(codeId); - var msgInstantiateCounterContract = new SecretNET.Tx.MsgInstantiateContract(codeId, $"MySimpleCouter {codeId}", initMsg); + var msgInitContract = new MsgInstantiateContract( + codeId: codeId, + label: $"MySimpleCouter {codeId}", + initMsg: new { count = 100 }, + codeHash: contractCodeHash); // optional but way faster - var instantiateCounterContractResponse = await secretClient.Tx.Compute.InstantiateContract(msgInstantiateCounterContract, txOptions: txOptionsUpload); - logSecretTx("InstantiateContract", instantiateCounterContractResponse); + var initContractResponse = await secretClient.Tx.Compute.InstantiateContract(msgInitContract, txOptions: txOptionsUpload); + logSecretTx("InstantiateContract", initContractResponse); + + contractAddress = initContractResponse?.Response?.Address; + + Console.WriteLine("Contract CodeHash: " + contractCodeHash); + Console.WriteLine("Contract Address: " + contractAddress); - contractAddress = instantiateCounterContractResponse.Response.Address; - if (!string.IsNullOrEmpty(contractAddress)) - { - contractCodeHash = await secretClient.Query.Compute.GetCodeHash(contractAddress); - Console.WriteLine("ContractCodeHash: " + contractCodeHash); - } } //Console.ReadLine(); @@ -239,7 +276,7 @@ Console.WriteLine("Execute : " + JsonConvert.SerializeObject(executeMsg, Formatting.Indented) + "\r\n"); -var msgExecuteContract = new SecretNET.Tx.MsgExecuteContract(contractAddress, executeMsg, contractCodeHash); +var msgExecuteContract = new MsgExecuteContract(contractAddress: contractAddress, msg: executeMsg, codeHash: contractCodeHash, sender: null, sentFunds: null); var executeContractResponse = await secretClient.Tx.Compute.ExecuteContract(msgExecuteContract, txOptionsExecute); logSecretTx("ExecuteContract", executeContractResponse); diff --git a/resources/mysimplecounter.wasm.gz b/resources/mysimplecounter.wasm.gz new file mode 100644 index 0000000000000000000000000000000000000000..fa0eda7efdaa3ceed93e48caf390de029cf1bcbf GIT binary patch literal 40961 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%0A2QWmEXXQ*~c5?(gjNwE>_WkHz7i28tq-w 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 literal 0 HcmV?d00001 diff --git a/resources/snip721_722_reference_impl.wasm.gz b/resources/snip721_722_reference_impl.wasm.gz new file mode 100644 index 0000000000000000000000000000000000000000..03e73a399ea2e4f1b7c367f02122cebe542036ec GIT binary patch literal 229903 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�gEboa50#YFlT&6`WI=!P_4 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 3) + if (!string.IsNullOrWhiteSpace(base64String) && base64String.Length > 3) { return _isBase64RegEx.IsMatch(base64String); } @@ -77,7 +77,7 @@ public static async Task CompressGzip(this byte[] data, CompressionLevel public static bool IsProtoType(this string typeUrl, string matchType) { - if (!String.IsNullOrEmpty(typeUrl) && !String.IsNullOrEmpty(matchType)) + if (!string.IsNullOrWhiteSpace(typeUrl) && !string.IsNullOrWhiteSpace(matchType)) { var cleanedType = CleanUpTypeurl(typeUrl); return cleanedType.Equals(matchType, StringComparison.InvariantCultureIgnoreCase); @@ -87,7 +87,7 @@ public static bool IsProtoType(this string typeUrl, string matchType) public static string CleanUpTypeurl(this string typeUrl) { - if (!String.IsNullOrEmpty(typeUrl)) + if (!string.IsNullOrWhiteSpace(typeUrl)) { return Regex.Match(typeUrl, "[^/]([^/]*)$").Groups[0].Value; } @@ -96,7 +96,7 @@ public static string CleanUpTypeurl(this string typeUrl) public static bool IsProtoType(this IMessage msg, string matchType) { - if (!String.IsNullOrEmpty(msg?.Descriptor?.FullName)) + if (!string.IsNullOrWhiteSpace(msg?.Descriptor?.FullName)) { return IsProtoType(msg.Descriptor.FullName, matchType); } diff --git a/src/Common/CreateClientOptions.cs b/src/Common/CreateClientOptions.cs index ba91df51..f31dacfb 100644 --- a/src/Common/CreateClientOptions.cs +++ b/src/Common/CreateClientOptions.cs @@ -54,7 +54,7 @@ public CreateClientOptions(string grpcWebUrl, string chainId, Wallet wallet = nu this.ChainId = chainId; this.Wallet = wallet; this.WalletAddress = walletAddress; - if (String.IsNullOrEmpty(walletAddress) && this.Wallet != null) + if (string.IsNullOrWhiteSpace(walletAddress) && this.Wallet != null) { this.WalletAddress = this.Wallet.Address; } diff --git a/src/Common/SecretEncryptionUtil.cs b/src/Common/SecretEncryptionUtil.cs index 62241bf9..824e6ffa 100644 --- a/src/Common/SecretEncryptionUtil.cs +++ b/src/Common/SecretEncryptionUtil.cs @@ -50,7 +50,7 @@ public SecretEncryptionUtils(string chainId, IRegistrationQueryClient registrati _privateKey = generatedKeyPair.PrivateKey; _publicKey = generatedKeyPair.PublicKey; - if (!String.IsNullOrEmpty(_chainId) && _mainnetChainIds.Contains(_chainId)) + if (!string.IsNullOrWhiteSpace(_chainId) && _mainnetChainIds.Contains(_chainId)) { // Major speedup // TODO: not sure if this is the best approach for detecting mainnet diff --git a/src/Crypto/DataEncoders/ASCIIEncoder.cs b/src/Crypto/DataEncoders/ASCIIEncoder.cs index 8d385ec4..9ca455c4 100644 --- a/src/Crypto/DataEncoders/ASCIIEncoder.cs +++ b/src/Crypto/DataEncoders/ASCIIEncoder.cs @@ -5,7 +5,7 @@ internal class ASCIIEncoder : DataEncoder //Do not using Encoding.ASCII (not portable) internal override byte[] DecodeData(string encoded) { - if (string.IsNullOrEmpty(encoded)) + if (string.IsNullOrWhiteSpace(encoded)) return new byte[0]; Span r = encoded.Length is int v && v > 256 ? new byte[v] : stackalloc byte[v]; diff --git a/src/Query/ComputeQueryClient.cs b/src/Query/ComputeQueryClient.cs index e5c5aeae..eb99e694 100644 --- a/src/Query/ComputeQueryClient.cs +++ b/src/Query/ComputeQueryClient.cs @@ -50,14 +50,14 @@ private Secret.Compute.V1Beta1.Query.QueryClient client /// SecretQueryContractResult<R>. public async Task> QueryContract(string contractAddress, object queryMsg, string codeHash = null, Metadata metadata = null) where R : class { - if (string.IsNullOrEmpty(codeHash)) + if (string.IsNullOrWhiteSpace(codeHash)) { codeHash = await GetCodeHash(contractAddress, metadata); } SecretQueryContractResult result = null; - if (!string.IsNullOrEmpty(codeHash)) + if (!string.IsNullOrWhiteSpace(codeHash)) { var queryMsgString = queryMsg is string ? queryMsg : JsonConvert.SerializeObject(queryMsg); var encryptedQuery = await Encryption.Encrypt(codeHash, queryMsgString); @@ -91,8 +91,8 @@ public async Task> QueryContract(string contract { var errorMatch = _errorMessageRegEx.Match(ex.Message); if (errorMatch.Success && - !string.IsNullOrEmpty(errorMatch.Groups["error"]?.Value) && - !string.IsNullOrEmpty(errorMatch.Groups["encrypted"]?.Value)) + !string.IsNullOrWhiteSpace(errorMatch.Groups["error"]?.Value) && + !string.IsNullOrWhiteSpace(errorMatch.Groups["encrypted"]?.Value)) { var encryptedError = Convert.FromBase64String(errorMatch.Groups["encrypted"].Value); var error = errorMatch.Groups["error"].Value; @@ -150,7 +150,7 @@ public async Task GetCodeHash(string contractAddress, Metadata metadata if (codeInfoResult != null && codeInfoResult.CodeId > 0) { var code = await Code(codeInfoResult.CodeId, metadata); - if (code.CodeInfo != null && !String.IsNullOrEmpty(code.CodeInfo?.CodeHash)) + if (code.CodeInfo != null && !string.IsNullOrWhiteSpace(code.CodeInfo?.CodeHash)) { if (!_codeHashCache.ContainsKey(contractAddress)) { @@ -178,7 +178,7 @@ public async Task GetCodeHashByCodeId(ulong codeId, Metadata metadata = } var code = await Code(codeId, metadata); - if (code.CodeInfo != null && !String.IsNullOrEmpty(code.CodeInfo.CodeHash)) + if (code.CodeInfo != null && !string.IsNullOrWhiteSpace(code.CodeInfo.CodeHash)) { if (!_codeHashCacheByCodeId.ContainsKey(codeId)) { diff --git a/src/Query/Queries.cs b/src/Query/Queries.cs index a324e5d2..d4ba2d9d 100644 --- a/src/Query/Queries.cs +++ b/src/Query/Queries.cs @@ -89,7 +89,7 @@ internal Service.ServiceClient ServiceClient /// SecretTx. public async Task GetTx(string hash, bool tryToDecrypt = true) { - var result = await TxsQuery($"tx.hash='{hash}'", tryToDecrypt); + var result = await TxsQuery($"tx.hash='{hash.ToUpper()}'", tryToDecrypt); return result?[0]; } @@ -392,7 +392,7 @@ await Parallel.ForEachAsync(txResponses, async (txResponse, cancellationToken) = var arrayLog = new List(); var rawLog = (string)tx.RawLog.Clone(); - if (tx.Code == 0 && !string.IsNullOrEmpty(tx.RawLog)) + if (tx.Code == 0 && !string.IsNullOrWhiteSpace(tx.RawLog)) { jsonLogs = JsonConvert.DeserializeObject>(tx.RawLog); @@ -444,14 +444,14 @@ await Parallel.ForEachAsync(txResponses, async (txResponse, cancellationToken) = } } } - else if (tx.Code != 0 && !string.IsNullOrEmpty(tx.RawLog)) + else if (tx.Code != 0 && !string.IsNullOrWhiteSpace(tx.RawLog)) { try { var errorMatches = _errorMessageRegEx.Match(tx.RawLog); if (errorMatches.Success && - !string.IsNullOrEmpty(errorMatches.Groups["msgIndex"]?.Value) && - !string.IsNullOrEmpty(errorMatches.Groups["encrypted"]?.Value)) + !string.IsNullOrWhiteSpace(errorMatches.Groups["msgIndex"]?.Value) && + !string.IsNullOrWhiteSpace(errorMatches.Groups["encrypted"]?.Value)) { var encryptedError = Convert.FromBase64String(errorMatches.Groups["encrypted"].Value); var msgIndex = Convert.ToInt32(errorMatches.Groups["msgIndex"].Value); diff --git a/src/SecretNetworkClient.cs b/src/SecretNetworkClient.cs index df2794ad..96d2ea22 100644 --- a/src/SecretNetworkClient.cs +++ b/src/SecretNetworkClient.cs @@ -44,9 +44,10 @@ public class SecretNetworkClient : ISecretNetworkClient ///

/// WalletAddress is the specific account address in the wallet that is permitted to sign transactions and permits. + /// If no wallet is attached the `CreateClientOptions.WalletAddress` is returned. /// /// The wallet address. - public string WalletAddress { get { return _wallet?.Address; } } + public string WalletAddress { get { return !string.IsNullOrWhiteSpace(_wallet?.Address) ? _wallet?.Address : _createClientOptions?.WalletAddress; } } /// /// Passing `encryptionSeed` will allow tx decryption at a later time. Ignored if `encryptionUtils` is supplied. @@ -500,7 +501,7 @@ private async Task PopulateCodeHash(Tx.MsgBase msg) if (msg.MsgType.IsProtoType(MsgGrantAuthorization.MsgExecuteContract)) { var msgExecuteContract = (Tx.MsgExecuteContract)msg; - if (String.IsNullOrEmpty(msgExecuteContract.CodeHash)) + if (string.IsNullOrWhiteSpace(msgExecuteContract.CodeHash)) { msgExecuteContract.CodeHash = await Query.Compute.GetCodeHash(msgExecuteContract.ContractAddress); } @@ -508,7 +509,7 @@ private async Task PopulateCodeHash(Tx.MsgBase msg) else if (msg.MsgType.IsProtoType(MsgGrantAuthorization.MsgInstantiateContract)) { var msgInstantiateContract = (Tx.MsgInstantiateContract)msg; - if (String.IsNullOrEmpty(msgInstantiateContract.CodeHash)) + if (string.IsNullOrWhiteSpace(msgInstantiateContract.CodeHash)) { msgInstantiateContract.CodeHash = await Query.Compute.GetCodeHashByCodeId(msgInstantiateContract.CodeId); } diff --git a/src/Tx/Compute.Msg.cs b/src/Tx/Compute.Msg.cs index ffbfbaa7..e1d8348d 100644 --- a/src/Tx/Compute.Msg.cs +++ b/src/Tx/Compute.Msg.cs @@ -52,7 +52,7 @@ public string GetMissingCodeHashWarning() public void WarnIfCodeHashMissing() { - if (String.IsNullOrEmpty(CodeHash)) + if (string.IsNullOrWhiteSpace(CodeHash)) { _warnCodeHash = true; Debug.WriteLine(GetMissingCodeHashWarning()); @@ -92,7 +92,7 @@ public MsgExecuteContract(string contractAddress, object msg, string codeHash = Msg = msg; SentFunds = sentFunds; - if (!String.IsNullOrEmpty(codeHash)) + if (!string.IsNullOrWhiteSpace(codeHash)) { CodeHash = codeHash.Replace("0x", "").ToLower(); } @@ -160,7 +160,7 @@ public MsgInstantiateContract(ulong codeId, string label, object initMsg, string InitFunds = initFunds; Sender = sender; - if (!String.IsNullOrEmpty(codeHash)) + if (!string.IsNullOrWhiteSpace(codeHash)) { CodeHash = codeHash.Replace("0x", "").ToLower(); } diff --git a/src/Tx/ComputeTx.cs b/src/Tx/ComputeTx.cs index b4f18657..73302964 100644 --- a/src/Tx/ComputeTx.cs +++ b/src/Tx/ComputeTx.cs @@ -17,7 +17,7 @@ internal ComputeTx(TxClient tx) /// public async Task ExecuteContract(MsgExecuteContract msg, TxOptions txOptions = null) { - if (string.IsNullOrEmpty(msg.Sender)) + if (string.IsNullOrWhiteSpace(msg.Sender)) { msg.Sender = _tx.secretClient.WalletAddress; } @@ -56,7 +56,7 @@ public async Task> ExecuteContract(MsgExecuteContract msg, msg.WasmByteCode = await msg.WasmByteCode.CompressGzip(); } - if (string.IsNullOrEmpty(msg.Sender)) + if (string.IsNullOrWhiteSpace(msg.Sender)) { msg.Sender = _tx.secretClient.WalletAddress; } @@ -73,7 +73,7 @@ public async Task> ExecuteContract(MsgExecuteContract msg, /// public async Task> InstantiateContract(MsgInstantiateContract msg, TxOptions txOptions = null) { - if (string.IsNullOrEmpty(msg.Sender)) + if (string.IsNullOrWhiteSpace(msg.Sender)) { msg.Sender = _tx.secretClient.WalletAddress; } diff --git a/src/Tx/MsgDecoderRegistry.cs b/src/Tx/MsgDecoderRegistry.cs index b14f1d20..afeb8d07 100644 --- a/src/Tx/MsgDecoderRegistry.cs +++ b/src/Tx/MsgDecoderRegistry.cs @@ -172,7 +172,7 @@ static MsgDecoderRegistry() public static Func Get(string typeUrl) { - if (!String.IsNullOrEmpty(typeUrl)) + if (!string.IsNullOrWhiteSpace(typeUrl)) { var regKey = typeUrl.CleanUpTypeurl().ToLower(); if (Registry.ContainsKey(regKey)) diff --git a/src/Tx/TxClient.cs b/src/Tx/TxClient.cs index 86fa3844..c83e944b 100644 --- a/src/Tx/TxClient.cs +++ b/src/Tx/TxClient.cs @@ -215,7 +215,7 @@ public IbcTransferTx IbcTransfer /// SimulateResponse. public async Task Simulate(MsgBase message, TxOptions txOptions = null) { - if (message is MsgExecuteContract && string.IsNullOrEmpty(((MsgExecuteContract)message).Sender)) + if (message is MsgExecuteContract && string.IsNullOrWhiteSpace(((MsgExecuteContract)message).Sender)) { ((MsgExecuteContract)message).Sender = WalletAddress; } @@ -223,7 +223,40 @@ public async Task Simulate(MsgBase message, TxOptions txOption } /// - /// Simulates the specified Tx / messages. + /// Simulates the specified message. + /// + /// The message. + /// The tx options. + /// SimulateResponse. + public async Task Simulate(IMessage message, TxOptions txOptions = null) + { + if (message is MsgExecuteContract && string.IsNullOrWhiteSpace(((MsgExecuteContract)message).Sender)) + { + ((MsgExecuteContract)message).Sender = WalletAddress; + } + return await Simulate(new IMessage[] { message }, txOptions); + } + + /// + /// Used to simulate a complex transactions, which contains a list of messages, without broadcasting it to the chain. Can be used to get a gas estimation or to see the output without actually committing a transaction on-chain. + /// The input should be exactly how you'd use it in Tx.Broadcast(), except that you don't have to pass in gasLimit, gasPriceInFeeDenom and feeDenom. + /// + /// The messages. + /// The tx options. + /// SimulateResponse. + public async Task Simulate(IMessage[] messages, TxOptions txOptions = null) + { + var msgBaseList = new List(); + foreach (IMessage msg in messages) + { + msgBaseList.Add(new Msg(msg)); + } + return await Simulate(msgBaseList.ToArray(), txOptions); + } + + /// + /// Used to simulate a complex transactions, which contains a list of messages, without broadcasting it to the chain. Can be used to get a gas estimation or to see the output without actually committing a transaction on-chain. + /// The input should be exactly how you'd use it in Tx.Broadcast(), except that you don't have to pass in gasLimit, gasPriceInFeeDenom and feeDenom. /// /// The messages. /// The tx options. @@ -232,7 +265,7 @@ public async Task Simulate(MsgBase[] messages, TxOptions txOpt { foreach (var msg in messages) { - if (msg is MsgExecuteContract && string.IsNullOrEmpty(((MsgExecuteContract)msg).Sender)) + if (msg is MsgExecuteContract && string.IsNullOrWhiteSpace(((MsgExecuteContract)msg).Sender)) { ((MsgExecuteContract)msg).Sender = WalletAddress; } @@ -271,7 +304,7 @@ public async Task Broadcast(IMessage message, TxOptions txOptions = nu } /// - /// Broadcasts the specified Tx / message. + /// Used to send a single transactions / messages. /// /// /// The message. @@ -283,7 +316,7 @@ public async Task> Broadcast(MsgBase message, TxOptions txO } /// - /// Broadcasts the specified Tx / messages. + /// Used to send a single transactions / messages. /// /// /// The message. @@ -295,7 +328,8 @@ public async Task> Broadcast(IMessage message, TxOptions tx } /// - /// Broadcasts the specified Tx / messages. + /// Used to send a complex transactions, which contains a list of messages. The messages are executed in sequence, and the transaction succeeds if all messages succeed. + /// The result tries to convert the first message result to T. /// /// /// The messages. @@ -312,7 +346,8 @@ public async Task> Broadcast(MsgBase[] messages, TxOptions } /// - /// Broadcasts the specified Tx / messages. + /// Used to send a complex transactions, which contains a list of messages. The messages are executed in sequence, and the transaction succeeds if all messages succeed. + /// The result tries to convert the first message result to T. /// /// /// The messages. @@ -329,7 +364,7 @@ public async Task> Broadcast(IMessage[] messages, TxOptions } /// - /// Broadcasts the specified Tx / messages. + /// Used to send a complex transactions, which contains a list of messages. The messages are executed in sequence, and the transaction succeeds if all messages succeed. /// /// The messages. /// The tx options. @@ -343,6 +378,23 @@ public async Task Broadcast(MsgBase[] messages, TxOptions txOptions = return await BroadcastTx(prepareResult, txOptions); } + /// + /// Used to send a complex transactions, which contains a list of messages. The messages are executed in sequence, and the transaction succeeds if all messages succeed. + /// + /// The messages. + /// The tx options. + /// SecretTx. + public async Task Broadcast(IMessage[] messages, TxOptions txOptions = null) + { + var msgBaseList = new List(); + foreach (IMessage msg in messages) + { + msgBaseList.Add(new Msg(msg)); + } + + return await Broadcast(msgBaseList.ToArray(), txOptions); + } + private async Task BroadcastTx(byte[] TxBytes, TxOptions txOptions) { var start = DateTime.Now;