import { ObservableChainQuery } from "../chain-query" ; import { KVStore , toGenerator } from "@keplr-wallet/common" ; import { ChainGetter } from "../../common" ; import { ObservableQuerySecretContractCodeHash } from "./contract-hash" ; import { autorun , computed , flow , makeObservable , observable } from "mobx" ; import { Keplr } from "@keplr-wallet/types" ; import Axios , { CancelToken } from "axios" ; import { QueryResponse } from "../../common" ; import { Buffer } from "buffer/" ; export class ObservableSecretContractChainQuery < T > extends ObservableChainQuery < T > { @ observable . ref protected keplr ? : Keplr = undefined ; protected nonce ? : Uint8Array ; @ observable protected _isIniting : boolean = false ; constructor ( kvStore , chainId , chainGetter , protected readonly apiGetter , protected readonly contractAddress , protected obj , protected readonly querySecretContractCodeHash ) { super ( kvStore , chainId , chainGetter , `` ) ; makeObservable ( this ) ; this . initKeplr ( ) ; const disposer = autorun ( ( ) => { if ( this . keplr && this . contractCodeHash ) { this . init ( ) ; disposer ( ) ; } } ) ; } protected setObj ( obj ) { this . obj = obj ; this . init ( ) ; } get isFetching ( ) : boolean { return ( this . querySecretContractCodeHash . getQueryContract ( this . contractAddress ) . isFetching || this . keplr == null || this . _isIniting || super . isFetching ) ; } protected canFetch ( ) { if ( ! this . querySecretContractCodeHash . getQueryContract ( this . contractAddress ) . response ) { return false ; } return this . contractAddress . length !== 0 && this . nonce != null ; } @ flow protected * initKeplr ( ) { this . keplr = yield * toGenerator ( this . apiGetter ( ) ) ; } @ flow protected * init ( ) { this . _isIniting = true ; if ( this . keplr && this . contractCodeHash ) { const enigmaUtils = this . keplr . getEnigmaUtils ( this . chainId ) ; const encrypted = yield * toGenerator ( enigmaUtils . encrypt ( this . contractCodeHash , this . obj ) ) ; this . nonce = encrypted . slice ( 0 , 32 ) ; const encoded = Buffer . from ( Buffer . from ( encrypted ) . toString ( "base64" ) ) . toString ( "hex" ) ; this . setUrl ( `/wasm/contract/${ this . contractAddress }/query/${ encoded }?encoding=hex` ) ; } this . _isIniting = false ; } protected async fetchResponse ( cancelToken ) { let response ; try { response = await super . fetchResponse ( cancelToken ) ; } catch ( e ) { if ( ! Axios . isCancel ( e ) && e . response ?. data ?. error ) { const encryptedError = e . response . data . error ; const errorMessageRgx = /rpc error: code = (.+) = encrypted: (.+): (.+)/g ; const rgxMatches = errorMessageRgx . exec ( encryptedError ) ; if ( rgxMatches != null && rgxMatches . length === 4 ) { const errorCipherB64 = rgxMatches [ 2 ] ; const errorCipherBz = Buffer . from ( errorCipherB64 , "base64" ) ; if ( this . keplr && this . nonce ) { const decrypted = await this . keplr . getEnigmaUtils ( this . chainId ) . decrypt ( errorCipherBz , this . nonce ) ; const errorStr = Buffer . from ( decrypted ) . toString ( ) ; throw new Error ( errorStr ) ; } } } throw e ; } const encResult = ( response . data as unknown ) as { height : string ; result : { smart : string ; } ; } | undefined ; if ( ! this . keplr ) { throw new Error ( "Keplr API not initialized" ) ; } if ( ! this . nonce ) { throw new Error ( "Nonce is unknown" ) ; } if ( ! encResult ) { throw new Error ( "Failed to get the response from the contract" ) ; } const decrypted = await this . keplr . getEnigmaUtils ( this . chainId ) . decrypt ( Buffer . from ( encResult . result . smart , "base64" ) , this . nonce ) ; const message = Buffer . from ( Buffer . from ( decrypted ) . toString ( ) , "base64" ) . toString ( ) ; const obj = JSON . parse ( message ) ; return { data : obj as T , status : response . status , staled : false , timestamp : Date . now ( ) , } ; } protected getCacheKey ( ) { return `${ this . instance . name }-${ this . instance . defaults . baseURL }${ this . instance . getUri ( { url : `/wasm/contract/${ this . contractAddress }/query/${ JSON . stringify ( this . obj ) }?encoding=json` , } ) }` ; } @ computed get contractCodeHash ( ) : string | undefined { const queryCodeHash = this . querySecretContractCodeHash . getQueryContract ( this . contractAddress ) ; if ( ! queryCodeHash . response ) { return undefined ; } return queryCodeHash . response . data . result ; } }