diff --git a/README.md b/README.md index 402d3d5f..14341657 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ describe('bank', () => { // get account info const account = await rest.cosmos.auth .account(sdk, fromAddress) - .then((res) => res.data.account && cosmosclient.codec.unpackAny(res.data.account)) + .then((res) => res.data.account && cosmosclient.codec.unpackCosmosAny(res.data.account)) .catch((_) => undefined); if (!(account instanceof cosmos.auth.v1beta1.BaseAccount)) { diff --git a/package-lock.json b/package-lock.json index abbbc129..fac38a58 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "cosmos-client", - "version": "0.42.10", + "version": "0.42.11", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "cosmos-client", - "version": "0.42.10", + "version": "0.42.11", "license": "ISC", "dependencies": { "axios": "^0.21.1", diff --git a/package.json b/package.json index 1ef4251b..81dd0a76 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "cosmos-client", "description": "REST API Client for Cosmos SDK Blockchain", - "version": "0.42.10", + "version": "0.42.11", "author": "LCNEM, Inc.", "bugs": { "url": "https://github.com/cosmos-client/cosmos-client-ts/issues" diff --git a/src/rest/cosmos/bank/bank.spec.ts b/src/rest/cosmos/bank/bank.spec.ts index 86634bf3..665279e4 100644 --- a/src/rest/cosmos/bank/bank.spec.ts +++ b/src/rest/cosmos/bank/bank.spec.ts @@ -20,7 +20,7 @@ describe('bank', () => { // get account info const account = await rest.cosmos.auth .account(sdk, fromAddress) - .then((res) => res.data.account && cosmosclient.codec.unpackAny(res.data.account)) + .then((res) => res.data.account && cosmosclient.codec.unpackCosmosAny(res.data.account)) .catch((_) => undefined); if (!(account instanceof cosmos.auth.v1beta1.BaseAccount)) { diff --git a/src/types/codec/codec.spec.ts b/src/types/codec/codec.spec.ts new file mode 100644 index 00000000..59045366 --- /dev/null +++ b/src/types/codec/codec.spec.ts @@ -0,0 +1,72 @@ +import { codec } from '.'; +import { cosmos } from '../../proto'; +import '../..'; + +describe('codec', () => { + it('unpack', () => { + expect.hasAssertions(); + const res = { + data: { + account: { + '@type': '/cosmos.auth.v1beta1.BaseAccount', + address: 'jpyx10lcj22kzftvnduchatmnsnhfljeky5ghd398wt', + pub_key: { '@type': '/cosmos.crypto.secp256k1.PubKey', key: 'AvSf2U/B23UZKvLTD0E4xqJ33Nn0Z552nXCkkwEfleiu' }, + account_number: '0', + sequence: '13', + }, + }, + status: 200, + statusText: 'OK', + headers: { 'content-length': '315', 'content-type': 'application/json' }, + config: { + url: 'http://a.test.jpyx.lcnem.net:1317/cosmos/auth/v1beta1/accounts/jpyx10lcj22kzftvnduchatmnsnhfljeky5ghd398wt', + method: 'get', + headers: { Accept: 'application/json, text/plain, */*' }, + transformRequest: [null], + transformResponse: [null], + timeout: 0, + xsrfCookieName: 'XSRF-TOKEN', + xsrfHeaderName: 'X-XSRF-TOKEN', + maxContentLength: -1, + maxBodyLength: -1, + }, + request: { + __zone_symbol__xhrSync: false, + __zone_symbol__xhrURL: 'http://a.test.jpyx.lcnem.net:1317/cosmos/auth/v1beta1/accounts/jpyx10lcj22kzftvnduchatmnsnhfljeky5ghd398wt', + __zone_symbol__readystatechangefalse: [ + { + type: 'eventTask', + state: 'scheduled', + source: 'XMLHttpRequest.addEventListener:readystatechange', + zone: 'angular', + runCount: 6, + }, + ], + __zone_symbol__abortfalse: [ + { type: 'eventTask', state: 'scheduled', source: 'XMLHttpRequest.addEventListener:abort', zone: 'angular', runCount: 0 }, + ], + __zone_symbol__errorfalse: [ + { type: 'eventTask', state: 'scheduled', source: 'XMLHttpRequest.addEventListener:error', zone: 'angular', runCount: 0 }, + ], + __zone_symbol__timeoutfalse: [ + { type: 'eventTask', state: 'scheduled', source: 'XMLHttpRequest.addEventListener:timeout', zone: 'angular', runCount: 0 }, + ], + __zone_symbol__xhrScheduled: true, + __zone_symbol__xhrErrorBeforeScheduled: false, + __zone_symbol__xhrTask: { type: 'macroTask', state: 'notScheduled', source: 'XMLHttpRequest.send', zone: 'angular', runCount: 0 }, + }, + }; + + const unpacked = codec.unpackCosmosAny(res.data.account); + if (!(unpacked instanceof cosmos.auth.v1beta1.BaseAccount)) { + throw Error(''); + } + + console.log(unpacked); + + const key = codec.unpackAny(unpacked.pub_key); + console.log(key) + + expect(true).toBeTruthy(); + }); +}); diff --git a/src/types/codec/config.ts b/src/types/codec/config.ts index 89762311..0af2d513 100644 --- a/src/types/codec/config.ts +++ b/src/types/codec/config.ts @@ -1,4 +1,8 @@ +import * as $protobuf from 'protobufjs'; + export const codecMaps = { inv: new Map(), + encode: {} as { [type: string]: (value: any) => $protobuf.Writer }, + decode: {} as { [type: string]: (value: Uint8Array) => unknown }, fromObject: {} as { [type: string]: (value: any) => unknown }, }; diff --git a/src/types/codec/module.ts b/src/types/codec/module.ts index be9b8585..723fa6c3 100644 --- a/src/types/codec/module.ts +++ b/src/types/codec/module.ts @@ -1,25 +1,83 @@ import { config } from '../../config'; import { google } from '../../proto'; +import * as $protobuf from 'protobufjs'; -export function register(type: string, constructor: Function & { fromObject(object: any): any }) { +export function register( + type: string, + constructor: Function & { encode(value: any): $protobuf.Writer; decode(value: Uint8Array): unknown; fromObject(object: any): any }, +) { config.codecMaps.inv.set(constructor, type); + config.codecMaps.encode[type] = constructor.encode; + config.codecMaps.decode[type] = constructor.decode; config.codecMaps.fromObject[type] = constructor.fromObject; } -export function unpackAny(value: any) { +/** + * CosmosAny -> Instance + * @param value + * @returns + */ +export function unpackCosmosAny(value: any): unknown { + const newValue: { [key: string]: any } = {}; + + for (const key in value) { + newValue[key] = packCosmosAny(value[key]); + } + const typeURL = value && value['@type']; - if (!typeURL) { - throw Error("The field '@type' is undefined"); + if (!typeURL || !config.codecMaps.fromObject[typeURL]) { + return newValue; } - if (!config.codecMaps.fromObject[typeURL]) { - throw Error('This type is not registered'); + return config.codecMaps.fromObject[typeURL](newValue); +} + +/** + * CosmosAny -> Any + * @param value + */ +export function packCosmosAny(value: any) { + const typeURL = value && value['@type']; + + if (!typeURL || !config.codecMaps.fromObject[typeURL]) { + return value; } - return config.codecMaps.fromObject[typeURL](value); + const newValue: { [key: string]: any } = {}; + + for (const key in value) { + newValue[key] = packCosmosAny(value[key]); + } + + return new google.protobuf.Any({ + type_url: typeURL, + value: config.codecMaps.encode[typeURL](config.codecMaps.fromObject[typeURL](newValue)).finish(), + }); +} + +/** + * Any -> Instance + * @param value + */ +export function unpackAny(value?: google.protobuf.IAny | null) { + if (!value) { + throw Error("Object 'value' is undefined"); + } + if (!value.type_url) { + throw Error("The field 'type_url' is undefined"); + } + if (!value.value) { + throw Error("The field 'value' is undefined"); + } + return config.codecMaps.decode[value.type_url](value.value); } +/** + * Instance -> Any + * @param value + * @returns + */ export function packAny(value: any) { const constructor = value?.constructor;