@@ -32,6 +32,195 @@ export function uint32ToString(val: number): string {
32
32
return Buffer . from ( buf ) . toString ( 'hex' ) ;
33
33
}
34
34
35
+ // https://github.com/confio/cosmjs-types/blob/66e52711914fccd2a9d1a03e392d3628fdf499e2/src/binary.ts#L60-L68
36
+ enum WireType {
37
+ Varint = 0 ,
38
+
39
+ Fixed64 = 1 ,
40
+
41
+ Bytes = 2 ,
42
+
43
+ Fixed32 = 5 ,
44
+ }
45
+
46
+ // https://github.com/confio/cosmjs-types/blob/66e52711914fccd2a9d1a03e392d3628fdf499e2/src/binary.ts#L496-L498
47
+ function indexOutOfRange ( reader : BinaryReader , writeLength ?: number ) {
48
+ return RangeError (
49
+ 'index out of range: ' + reader . pos + ' + ' + ( writeLength || 1 ) + ' > ' + reader . len
50
+ ) ;
51
+ }
52
+
53
+ // adapted from https://github.com/confio/cosmjs-types/blob/66e52711914fccd2a9d1a03e392d3628fdf499e2/src/binary.ts#L96
54
+ export class BinaryReader {
55
+ buf : Uint8Array ;
56
+ pos : number ;
57
+ type : number ;
58
+ len : number ;
59
+
60
+ assertBounds ( ) : void {
61
+ if ( this . pos > this . len ) throw new RangeError ( 'premature EOF' ) ;
62
+ }
63
+
64
+ constructor ( buf : Uint8Array ) {
65
+ this . buf = buf ;
66
+ this . pos = 0 ;
67
+ this . type = 0 ;
68
+ this . len = this . buf . length ;
69
+ }
70
+
71
+ /**
72
+ * Read an unsigned 32 bit varint.
73
+ *
74
+ * See https://github.com/protocolbuffers/protobuf/blob/8a71927d74a4ce34efe2d8769fda198f52d20d12/js/experimental/runtime/kernel/buffer_decoder.js#L220
75
+ */
76
+ varint32read ( ) : number {
77
+ let b = this . buf [ this . pos ++ ] ;
78
+ let result = b & 0x7f ;
79
+ if ( ( b & 0x80 ) == 0 ) {
80
+ this . assertBounds ( ) ;
81
+ return result ;
82
+ }
83
+
84
+ b = this . buf [ this . pos ++ ] ;
85
+ result |= ( b & 0x7f ) << 7 ;
86
+ if ( ( b & 0x80 ) == 0 ) {
87
+ this . assertBounds ( ) ;
88
+ return result ;
89
+ }
90
+
91
+ b = this . buf [ this . pos ++ ] ;
92
+ result |= ( b & 0x7f ) << 14 ;
93
+ if ( ( b & 0x80 ) == 0 ) {
94
+ this . assertBounds ( ) ;
95
+ return result ;
96
+ }
97
+
98
+ b = this . buf [ this . pos ++ ] ;
99
+ result |= ( b & 0x7f ) << 21 ;
100
+ if ( ( b & 0x80 ) == 0 ) {
101
+ this . assertBounds ( ) ;
102
+ return result ;
103
+ }
104
+
105
+ // Extract only last 4 bits
106
+ b = this . buf [ this . pos ++ ] ;
107
+ result |= ( b & 0x0f ) << 28 ;
108
+
109
+ for ( let readBytes = 5 ; ( b & 0x80 ) !== 0 && readBytes < 10 ; readBytes ++ )
110
+ b = this . buf [ this . pos ++ ] ;
111
+
112
+ if ( ( b & 0x80 ) != 0 ) throw new Error ( 'invalid varint' ) ;
113
+
114
+ this . assertBounds ( ) ;
115
+
116
+ // Result can have 32 bits, convert it to unsigned
117
+ return result >>> 0 ;
118
+ }
119
+
120
+ skip ( length ?: number ) {
121
+ if ( typeof length === 'number' ) {
122
+ if ( this . pos + length > this . len ) throw indexOutOfRange ( this , length ) ;
123
+ this . pos += length ;
124
+ } else {
125
+ do {
126
+ if ( this . pos >= this . len ) throw indexOutOfRange ( this ) ;
127
+ } while ( this . buf [ this . pos ++ ] & 128 ) ;
128
+ }
129
+ return this ;
130
+ }
131
+
132
+ skipType ( wireType : number ) {
133
+ switch ( wireType ) {
134
+ case WireType . Varint :
135
+ this . skip ( ) ;
136
+ break ;
137
+ case WireType . Fixed64 :
138
+ this . skip ( 8 ) ;
139
+ break ;
140
+ case WireType . Bytes :
141
+ this . skip ( this . uint32 ( ) ) ;
142
+ break ;
143
+ case 3 :
144
+ while ( ( wireType = this . uint32 ( ) & 7 ) !== 4 ) {
145
+ this . skipType ( wireType ) ;
146
+ }
147
+ break ;
148
+ case WireType . Fixed32 :
149
+ this . skip ( 4 ) ;
150
+ break ;
151
+
152
+ /* istanbul ignore next */
153
+ default :
154
+ throw Error ( 'invalid wire type ' + wireType + ' at offset ' + this . pos ) ;
155
+ }
156
+ return this ;
157
+ }
158
+
159
+ uint32 ( ) : number {
160
+ return this . varint32read ( ) ;
161
+ }
162
+
163
+ bytes ( ) : Uint8Array {
164
+ const len = this . uint32 ( ) ,
165
+ start = this . pos ;
166
+ this . pos += len ;
167
+ this . assertBounds ( ) ;
168
+ return this . buf . subarray ( start , start + len ) ;
169
+ }
170
+ }
171
+
172
+ // https://github.com/confio/cosmjs-types/blob/66e52711914fccd2a9d1a03e392d3628fdf499e2/src/cosmwasm/wasm/v1/query.ts#L111-L118
173
+ /**
174
+ * QuerySmartContractStateResponse is the response type for the
175
+ * Query/SmartContractState RPC method
176
+ */
177
+ export interface QuerySmartContractStateResponse {
178
+ /** Data contains the json data returned from the smart contract */
179
+ data : Uint8Array ;
180
+ }
181
+
182
+ // https://github.com/confio/cosmjs-types/blob/66e52711914fccd2a9d1a03e392d3628fdf499e2/src/cosmwasm/wasm/v1/query.ts#L855-L859
183
+ function createBaseQuerySmartContractStateResponse ( ) : QuerySmartContractStateResponse {
184
+ return {
185
+ data : new Uint8Array ( ) ,
186
+ } ;
187
+ }
188
+
189
+ // https://github.com/confio/cosmjs-types/blob/66e52711914fccd2a9d1a03e392d3628fdf499e2/src/cosmwasm/wasm/v1/query.ts#L871-L887
190
+ const QuerySmartContractStateResponse = {
191
+ decode ( input : BinaryReader | Uint8Array , length ?: number ) : QuerySmartContractStateResponse {
192
+ const reader = input instanceof BinaryReader ? input : new BinaryReader ( input ) ;
193
+ let end = length === undefined ? reader . len : reader . pos + length ;
194
+ const message = createBaseQuerySmartContractStateResponse ( ) ;
195
+ while ( reader . pos < end ) {
196
+ const tag = reader . uint32 ( ) ;
197
+ switch ( tag >>> 3 ) {
198
+ case 1 :
199
+ message . data = reader . bytes ( ) ;
200
+ break ;
201
+ default :
202
+ reader . skipType ( tag & 7 ) ;
203
+ break ;
204
+ }
205
+ }
206
+ return message ;
207
+ } ,
208
+ } ;
209
+
210
+ // https://github.com/cosmos/cosmjs/blob/e819a1fc0e99a3e5320d8d6667a08d3b92e5e836/packages/encoding/src/utf8.ts#L15-L25
211
+ /**
212
+ * Takes UTF-8 data and decodes it to a string.
213
+ *
214
+ * In lossy mode, the [REPLACEMENT CHARACTER](https://en.wikipedia.org/wiki/Specials_(Unicode_block))
215
+ * is used to substitude invalid encodings.
216
+ * By default lossy mode is off and invalid data will lead to exceptions.
217
+ */
218
+ export function fromUtf8 ( data : Uint8Array , lossy = false ) : string {
219
+ const fatal = ! lossy ;
220
+ return new TextDecoder ( 'utf-8' , { fatal } ) . decode ( data ) ;
221
+ }
222
+
223
+ // akin to https://github.com/cosmos/cosmjs/blob/e819a1fc0e99a3e5320d8d6667a08d3b92e5e836/packages/cosmwasm-stargate/src/modules/wasm/queries.ts#L135-L150
35
224
export async function queryContractSmart (
36
225
rpc : string ,
37
226
address : string ,
@@ -61,10 +250,20 @@ export async function queryContractSmart(
61
250
throw new Error ( `Query failed with unknown error` ) ;
62
251
}
63
252
}
64
- const asciiResponse = Buffer . from ( response . data . result . response . value , 'base64' ) . toString (
65
- 'ascii'
66
- ) ;
67
- return JSON . parse (
68
- asciiResponse . substring ( asciiResponse . indexOf ( '{' ) || asciiResponse . indexOf ( '[' ) )
253
+ // decode like https://github.com/confio/cosmjs-types/blob/66e52711914fccd2a9d1a03e392d3628fdf499e2/src/cosmwasm/wasm/v1/query.ts#L1621
254
+ const { data : responseData } = QuerySmartContractStateResponse . decode (
255
+ Buffer . from ( response . data . result . response . value , 'base64' )
69
256
) ;
257
+ // By convention, smart queries must return a valid JSON document (see https://github.com/CosmWasm/cosmwasm/issues/144)
258
+ let responseText : string ;
259
+ try {
260
+ responseText = fromUtf8 ( responseData ) ;
261
+ } catch ( error ) {
262
+ throw new Error ( `Could not UTF-8 decode smart query response from contract: ${ error } ` ) ;
263
+ }
264
+ try {
265
+ return JSON . parse ( responseText ) ;
266
+ } catch ( error ) {
267
+ throw new Error ( `Could not JSON parse smart query response from contract: ${ error } ` ) ;
268
+ }
70
269
}
0 commit comments