Git Product home page Git Product logo

telescope's Introduction

Telescope 🔭

A "babel for the Cosmos", Telescope is a TypeScript Transpiler for Cosmos Protobufs. Telescope is used to generate libraries for Cosmos blockchains. Simply point to your protobuffer files and create developer-friendly Typescript libraries for teams to build on your blockchain.

The following blockchain libraries (generated by Telescope) are available via npm

🎥 Checkout our video course to learn how to use telescope!

Table of contents

Quickstart

Follow the instructions below to generate a new Typescript package that you can publish to npm.

First, install telescope and create-cosmos-app

npm install -g @cosmology/telescope create-cosmos-app

Generate

Use the create-cosmos-app command to create a new package from the telescope boilerplate.

cca --boilerplate telescope

Then, you'll navigate into ./your-project/packages/telescope package for the next steps.

Add Protobufs

If you have .proto files, simply add them to a ./proto folder.

However, if you want to get started quickly using existing protos from our registry, simply use the install command.

telescope install

It's not necessary, but you may also specify specific packages, e.g.

telescope install @protobufs/osmosis

Transpile

To create the Typescript files for your chain, run the yarn codegen command inside of the package.

yarn codegen

Transpile with CLI

Less recommended, but you can also use our CLI for transpilation. To create the Typescript files with the cli, run the transpile command.

telescope transpile

You should now see some .ts files generated in ./src. These are the real source files used in your application.

Examples:

# Telescope takes chain1 folder as input,
# and generate files in 'gen/src' folder.
telescope transpile --protoDirs ../../__fixtures__/chain1 --outPath gen/src
# Telescope takes chain1 folder as input,
# and generate files in 'gen/src' folder using default telescope options.
telescope transpile --protoDirs ../../__fixtures__/chain1 --outPath gen/src --useDefaults
# Telescope takes chain1 folder(from args) and chain2 folder(from config) as input,
# and generate files in 'gen/src'(defined in the config file, will override outPath in args) folder using a config file.
# Note: --config will override --useDefaults.
telescope transpile --protoDirs ../../__fixtures__/chain1 --config .telescope.json
# Telescope takes more than one config. The config afterward will override those in front. In this case values in .telescope-ext.json will override those in .telescope.json.
telescope transpile --config .telescope.json --config .telescope-ext.json
//.telescope.json
{
  "protoDirs": [
    "../../fixtures/chain2"
  ],
  "outPath": "gen/src",
  "options": {
    // telescope options
    ...
  }
}

Build

Finally, run install and build to generate the JS and types for publishing your module to npm.

yarn build

Publishing

Now you should have code inside of your ./src folder, ready for publshing. If you used the create-cosmos-app boilerplate, use lerna to publish (and/or read the README in the boilerplate for instructions), or run npm publish from your repository.

Usage

Programatic Usage

First add telescope to your devDependencies:

yarn add --dev @cosmology/telescope

Install helpers and cosmjs dependencies listed here

import { join } from 'path';
import telescope from '@cosmology/telescope';
import { sync as rimraf } from 'rimraf';

const protoDirs = [join(__dirname, '/../proto')];
const outPath = join(__dirname, '../src');
rimraf(outPath);

telescope({
  protoDirs,
  outPath,

  // all options are totally optional ;)
  options: {
    aminoEncoding: {
        enabled: true
    },
    lcdClients: {
        enabled: false
    },
    rpcClients: {
        enabled: false,
        camelCase: true
    },

    // you can scope options to certain packages:
    packages: {
      nebula: {
        prototypes: {
          typingsFormat: {
            useExact: false
          }
        }
      },
      akash: {
        stargateClients: {
            enabled: true,
            includeCosmosDefaultTypes: false
        },
        prototypes: {
          typingsFormat: {
              useExact: false
          }
        }
      }
    }
  }
}).then(() => {
  console.log('✨ all done!');
}).catch(e => {
  console.error(e);
  process.exit(1);
})

Options

Amino Encoding

option description defaults
aminoEncoding.enabled generate amino types and amino converters true
aminoEncoding.omitEmptyTags An array of strings that determines whether a field should be omitted when serialized to JSON. If the array includes "omitempty", any field with the "omitempty" option in either gogoproto.jsontag or cosmos_proto.json_tag will be omitted. If the array includes "dont_omitempty", the field will be omitted or not based on the value of "(amino.dont_omitempty)": if it's null or false, the field will be omitted; if it's true, the field will not be omitted. ["omitempty", "dont_omitempty"]
aminoEncoding.disableMsgTypes disable generating AminoMsg types false
aminoEncoding.casingFn set the amino-casing function for a project snake()
aminoEncoding.exceptions set specific aminoType name exceptions see code
aminoEncoding.typeUrlToAmino create functions for aminoType name exceptions undefined
aminoEncoding.useLegacyInlineEncoding @deprecated. To use legacy inline encoding instead of using v2 recursive encoding false
aminoEncoding.useRecursiveV2encoding this's been removed. See useLegacyInlineEncoding instead.
aminoEncoding.legacy.useNullHandling handle null case when generating legacy amino converters(those in tx.amino.ts)
aminoEncoding.legacy.useOmitEmpty handle omit empty or not when generating legacy amino converters(those in tx.amino.ts)

Implemented Interface Options

option description defaults
interfaces.enabled enables converters convert between Any type and specific implemented interfaces. true
interfaces.useGlobalDecoderRegistry enables GlobalDecoderRegistry and related functions. Highly recommended to enable when dealing with fields with 'accepted_interface' option. Please see 'packages/telescope/tests/impl-interfaces.test.ts' for usage. false
interfaces.useUseInterfacesParams decides if add useInterfaces argument to decode and toAmino functions. false
interfaces.useByDefault decides if interface decoders are used by default (default for useInterfaces argument to decode and toAmino functions). true
interfaces.useByDefaultRpc decides if interface decoders are used by default by the RPC clients. true
interfaces.useUnionTypes Generate Any type as union types(TextProposal | RegisterIncentiveProposal) instead of intersection types(TextProposal & RegisterIncentiveProposal). false

Prototypes Options

option description defaults
prototypes.enabled enables the generation of proto encoding methods true
prototypes.includePackageVar export a protoPackage variable to indicate package name false
prototypes.includes.packages include a set of packages when transpilation. (if a package both meet include and exclude, it'll be excluded) undefined
prototypes.includes.protos include a set of proto files when transpilation. (if a proto both meet include and exclude, it'll be excluded) undefined
prototypes.excluded.packages exclude a set of packages from transpilation undefined
prototypes.excluded.protos try to exclude a set of proto files from transpilation. if files inside the list are dependencies to other files, they'll be still transpiled. undefined
prototypes.excluded.hardProtos exclude a set of proto files from transpilation. Files in this list will be excluded no matter it is a dependency to other files or not. undefined
prototypes.fieldDefaultIsOptional boolean value representing default optionality of field false
prototypes.useOptionalNullable use (gogoproto.nullable) values in determining optionality true
prototypes.allowUndefinedTypes boolean value allowing Types to be undefined false
prototypes.allowEncodeDefaultScalars boolean value allowing encoders encoding default values of scalar types. e.g. empty string, 0 or false false
prototypes.optionalQueryParams boolean value setting queryParams to be optional false
prototypes.optionalPageRequests boolean value setting PageRequest fields to optional false
prototypes.addTypeUrlToDecoders add $typeUrl field to generated interfaces true
prototypes.addAminoTypeToObjects add aminoType field to generated Decoders false
prototypes.addTypeUrlToObjects add typeUrl field to generated Decoders true
prototypes.enableRegistryLoader generate Registry loader to *.registry.ts files true
prototypes.enableMessageComposer generate MessageComposer to *.registry.ts files true
prototypes.patch An object mapping filenames to an array of Operation to be applied as patches to proto files during generation. See JSON Patch Protos undefined

Prototypes Methods

option description defaults
prototypes.methods.encode boolean to enable encode method on proto objects true
prototypes.methods.decode boolean to enable decode method on proto objects true
prototypes.methods.fromJSON boolean to enable fromJSON method on proto objects true
prototypes.methods.toJSON boolean to enable toJSON method on proto objects true
prototypes.methods.fromPartial boolean to enable fromPartial method on proto objects true
prototypes.methods.fromSDK boolean to enable fromSDK method on proto objects false
prototypes.methods.toSDK boolean to enable toSDK method on proto objects false

Enums Options

option description defaults
enums.useCustomNames Enables the usage of custom names for enums if specified through proto options or annotations, allowing for more descriptive or project-specific naming conventions. false

LCD Client Options

option description defaults
lcdClients.enabled generate LCD clients that can query proto Query messages true
lcdClients.bundle will generate factory bundle aggregate of all LCD Clients true
lcdClients.scoped will generate factory of scoped LCD Clients undefined
lcdClients.scopedIsExclusive will allow both scoped bundles and all RPC Clients true

See LCD Clients for more info.

RPC Client Options

option description defaults
rpcClients.type will generate this type of RPC client (tendermint, gRPC-web, gRPC) tendermint
rpcClients.enabled generate RPC clients that can interact with proto messages true
rpcClients.bundle will generate factory bundle aggregate of all RPC Clients true
rpcClients.camelCase use camel-case for RPC methods when generating RPC clients true
rpcClients.scoped will generate factory of scoped RPC Clients undefined
rpcClients.scopedIsExclusive will allow both scoped bundles and all RPC Clients true
rpcClients.enabledServices which services to enable [Msg,Query,Service]
rpcClients.instantOps will generate instant rpc operations in the file service-ops.ts under root folder, which contains customized classes having selected rpc methods undefined
rpcClients.serviceImplement assign implement type of rpc methods, Query or Tx, by setting patterns under service types. undefined

See RPC Clients for more info.

Stargate Client Options

option description defaults
stargateClients.includeCosmosDefaultTypes if true, will include the cosmjs defaults with stargate clients true (except cosmos package)
stargateClients.addGetTxRpc if true, will add getSigningTxRpc to clients in namespaces false

State Management

React Query

option description defaults
reactQuery.enabled if true, will create react hooks that use @tanstack/react-query hooks false
reactQuery.needExtraQueryKey if true, users can input extra react query key to some customized hooks. e.g.['rpcEndpoint', 'yourExtraKey'] false
reactQuery.include.protos if set, will create the hooks on matched proto filenames or patterns using minimatch []
reactQuery.include.packages if set, will create the hooks on matched packages files using minimatch []
reactQuery.include.patterns if set, will create the hooks on matched patterns of files using minimatch(deprecated in favor of packages and protos have been supported minimatch) []
reactQuery.instantExport.include.patterns if set, will expose instant hooks on matched patterns of packages + method(e.g. cosmos.bank.v1beta1.useBalance) using minimatch. If there're duplicated method names in multiple packages without setting reactQuery.instantExport.nameMapping, one duplicated name will created like: useCosmosBankV1beta1Balance []
reactQuery.instantExport.nameMapping map an alias to a package + method in case of better naming of duplicated method names. (e.g. useBankBalance: cosmos.bank.v1beta1.useBalance) Customized hook name is set in front of pkg+method, by doing this we can prevent duplicate alias. {}

Mobx

option description defaults
mobx.enabled if true, will create mobx stores that use mobx false
mobx.include.protos if set, will create the mobx stores on matched proto filenames or patterns using minimatch []
mobx.include.packages if set, will create the mobx stores on matched packages files using minimatch []
mobx.include.patterns if set, will create the mobx stores on matched patterns of proto files using minimatch(deprecated in favor of packages and protos have been supported minimatch) []

Pinia

option description defaults
pinia.enabled if true, will create pinia stores that use pinia false
mobx.include.protos if set, will create the pinia stores on matched proto filenames or patterns using minimatch []
mobx.include.packages if set, will create the pinia stores on matched packages files using minimatch []
mobx.include.patterns if set, will create the pinia stores on matched patterns of proto files using minimatch(deprecated in favor of packages and protos have been supported minimatch) []

Typings and Formating

option description defaults
prototypes.typingsFormat.customTypes.useCosmosSDKDec enable handling "prototypes.typingsFormat.customTypes.useCosmosSDKDec" proto custom type. Used to show decimal fields with the custom type correctly. Highly recommend set to true. true
prototypes.typingsFormat.num64 'long' or 'bigint', the way of generating int64 proto types, set to 'bigint' to enable using more stable built-in type bigint
prototypes.typingsFormat.useTelescopeGeneratedType Discard GeneratedType from cosmjs, use TelescopeGeneratedType instead inside *.registry.ts files false
prototypes.typingsFormat.useDeepPartial defaults to true, but if disabled uses the Partial TS type false
prototypes.typingsFormat.useExact defaults to false, but if enabled uses the Exact TS type false
prototypes.typingsFormat.toJsonUnknown defaults to true, but if disabled uses the JsonSafe for toJSON methods true
prototypes.typingsFormat.timestamp use either date or timestamp for Timestamp proto type "date"
prototypes.typingsFormat.duration use either duration or string for Duration proto type "duration"
prototypes.typingsFormat.setDefaultEnumToUnrecognized false: enum empty value would be 0, true: -1(value for enum unrecognized) true
prototypes.typingsFormat.setDefaultCustomTypesToUndefined true: Timestamp,Duration,Any,Coin empty value would be undefined., false: using fromPartial to get an empty obj false
prototypes.typingsFormat.autoFixUndefinedEnumDefault the default value of an enum field would be: 1(proto2); 0(proto3); But in some rare cases, those default values are not existing. But enable this, default value will automatically fixed with the smallest value. false

Protobuf parser

option description defaults
prototypes.parser.keepCase passes keepCase to protobuf parse() to keep original casing true
prototypes.parser.alternateCommentMode passes alternateCommentMode to protobuf parse() method true
prototypes.parser.preferTrailingComment passes preferTrailingComment to protobuf parse() method false

Typescript Disabling

option description defaults
tsDisable.disableAll if true, will include //@ts-nocheck on every output file false
tsDisable.patterns if set, will include //@ts-nocheck on matched patterns []
tsDisable.files if set, will include //@ts-nocheck on matched files []

ESLint Disabling

option description defaults
eslintDisable.disableAll if true, will include /* eslint-disable */ on every output file false
eslintDisable.patterns if set, will include /* eslint-disable */ on matched patterns []
eslintDisable.files if set, will include /* eslint-disable */ on matched files []

Bundle

option description defaults
bundle.enabled bundle all files into a scoped index file true

Output

option description defaults
env 'default' or 'v-next', set to 'v-next' to enable yet to release features default
removeUnusedImports removes unused imports true
classesUseArrowFunctions classes use arrow functions instead of bind()ing in constructors false
includeExternalHelpers exports a few helpers functions in extern.ts false
restoreImportExtension restore extensions of imported paths. e.g: '.js'. null means no ext null

Types

Timestamp

The representation of google.protobuf.Timestamp is configurable by the prototypes.typingsFormat.timestamp option.

Protobuf type Default/date='date' date='timestamp'
google.protobuf.Timestamp Date { seconds: Long, nanos: number }

TODO

  • add date='string' option

Duration

The representation of google.protobuf.Duration is configurable by the prototypes.typingsFormat.duration option.

Protobuf type Default/duration='duration' duration='string'
google.protobuf.Duration { seconds: Long, nanos: number } string

Composing Messages

This example shows messages from the osmojs, which was built with Telescope.

Import the osmosis object from osmojs. In this case, we're show the messages available from the osmosis.gamm.v1beta1 module:

import { osmosis } from 'osmojs';

const {
    joinPool,
    exitPool,
    exitSwapExternAmountOut,
    exitSwapShareAmountIn,
    joinSwapExternAmountIn,
    joinSwapShareAmountOut,
    swapExactAmountIn,
    swapExactAmountOut
} = osmosis.gamm.v1beta1.MessageComposer.withTypeUrl;

Now you can construct messages. If you use vscode or another typescript-enabled IDE, you should also be able to use ctrl+space to see auto-completion of the fields required for the message.

import { coin } from '@cosmjs/amino';

const msg = swapExactAmountIn({
  sender,
  routes,
  tokenIn: coin(amount, denom),
  tokenOutMinAmount
});

Calculating Fees

Make sure to create a fee object in addition to your message.

import { coins } from '@cosmjs/amino';

const fee = {
    amount: coins(0, 'uosmo'),
    gas: '250000'
}

if you are broadcasting multiple messages in a batch, you should simulate your tx and estimate the fee

import { Dec, IntPretty } from '@keplr-wallet/unit';

const gasEstimated = await stargateClient.simulate(address, msgs, memo);
const fee = {
  amount: coins(0, 'uosmo'),
  gas: new IntPretty(new Dec(gasEstimated).mul(new Dec(1.3)))
    .maxDecimals(0)
    .locale(false)
    .toString()
};

Stargate Clients

Every module gets their own signing client. This example demonstrates for the osmosis module.

Use getSigningOsmosisClient to get your SigningStargateClient, with the Osmosis proto/amino messages full-loaded. No need to manually add amino types, just require and initialize the client:

import { getSigningOsmosisClient } from 'osmojs';

const client = await getSigningOsmosisClient({
  rpcEndpoint,
  signer // OfflineSigner
});

Creating Signers

To broadcast messages, you'll want to use either keplr or an OfflineSigner from cosmjs using mnemonics.

Amino Signer

Likely you'll want to use the Amino, so unless you need proto, you should use this one:

import { getOfflineSigner as getOfflineSignerAmino } from 'cosmjs-utils';

Proto Signer

import { getOfflineSigner as getOfflineSignerProto } from 'cosmjs-utils';

WARNING: NOT RECOMMENDED TO USE PLAIN-TEXT MNEMONICS. Please take care of your security and use best practices such as AES encryption and/or methods from 12factor applications.

import { chains } from 'chain-registry';

const mnemonic =
    'unfold client turtle either pilot stock floor glow toward bullet car science';
const chain = chains.find(({ chain_name }) => chain_name === 'osmosis');
const signer = await getOfflineSigner({
    mnemonic,
    chain
});

Broadcasting messages

Now that you have your client, you can broadcast messages:

import { signAndBroadcast } from '@osmosnauts/helpers';

const res = await signAndBroadcast({
  client, // SigningStargateClient
  chainId: 'osmosis-1', // use 'osmo-test-4' for testnet
  address,
  msgs: [msg],
  fee,
  memo: ''
});

LCD Clients

For querying data via REST endpoints, you can use LCD Clients. For a better developer experience, you can generate a factory of scoped bundles of all LCD Clients with the lcdClients option.

const options: TelescopeOptions = {
    lcdClients: {
        enabled: true,
    },
};

If you use the lcdClients.scoped array, you can scope to only the modules of your interest.

const options: TelescopeOptions = {
  lcdClients: {
    enabled: true,
    scoped: [
      {
        dir: 'osmosis',
        filename: 'custom-lcd-client.ts',
        packages: [
          'cosmos.bank.v1beta1',
          'cosmos.gov.v1beta1',
          'osmosis.gamm.v1beta1'
        ],
        addToBundle: true,
        methodName: 'createCustomLCDClient'
      },
      {
        dir: 'evmos',
        filename: 'custom-lcd-client.ts',
        packages: [
          'cosmos.bank.v1beta1',
          'cosmos.gov.v1beta1',
          'evmos.erc20.v1'
        ],
        addToBundle: true,
        methodName: 'createEvmosLCDClient'
      }
    ]
  }
};

This will generate a nice helper in the ClientFactory, which you can then use to query multiple modules from a single object:

import { osmosis } from './codegen';

const main = async () => {
   const client = await osmosis.ClientFactory.createLCDClient({ restEndpoint: REST_ENDPOINT });

   // now you can query the modules
   const pool = await client.osmosis.gamm.v1beta1.pool({ poolId: "1" });
   const balance = await client.cosmos.bank.v1beta1.allBalances({ address: 'osmo1addresshere' });
};

LCD Clients Classes

If you want to instantiate a single client, for any module that has a Query type, there will be a LCDQueryClient object:

import { osmosis } from "osmojs";

export const main = async () => {
    const requestClient = new LCDClient({ restEndpoint: REST_ENDPOINT });
    const client = new osmosis.gamm.v1beta1.LCDQueryClient({ requestClient });
    const pools = await client.pools();
    console.log(pools);
};

main().then(() => {
    console.log('all done')
})

RPC Clients

Tendermint Client

For querying data via RPC endpoints, you can use RPC Clients. For a better developer experience, you can generate a factory of scoped bundles of all RPC Clients with the rpcClients option.

const options: TelescopeOptions = {
  rpcClients: {
    type: 'tendermint',
    enabled: true,
    camelCase: true
  }
};

If you use the rpcClients.scoped array, you can scope to only the modules of your interest. gRPC-web and gRPC-gateway work the same way with this option.

const options: TelescopeOptions = {
  rpcClients: {
    enabled: true,
    camelCase: true,
    scoped: [
      {
        dir: 'osmosis',
        filename: 'osmosis-rpc-client.ts',
        packages: [
          'cosmos.bank.v1beta1',
          'cosmos.gov.v1beta1',
          'osmosis.gamm.v1beta1'
        ],
        addToBundle: true,
        methodNameQuery: 'createRPCQueryClient',
        methodNameTx: 'createRPCTxClient'
      }
    ]
  }
};

This will generate helpers createRPCQueryClient and createRPCTxClient in the ClientFactory, which you can then use to query multiple modules from a single object:

import { osmosis } from './codegen';

const main = async () => {
  const client = await osmosis.ClientFactory.createRPCQueryClient({ rpcEndpoint });

  // now you can query the modules
  const pool = await client.osmosis.gamm.v1beta1.pool({ poolId: "1" });
  const balance = await client.cosmos.bank.v1beta1.allBalances({ address: 'osmo1addresshere' });
};

gRPC-web Client

For querying data via gRPC-web endpoints, you can use gRPC-web Clients. For a better developer experience, you can generate a factory of scoped bundles of all gRPC-web Clients with the rpcClients option.

const options: TelescopeOptions = {
  rpcClients: {
    type: 'grpc-web',
    enabled: true,
    camelCase: true
  }
};

This will generate helpers createGrpcWebClient and createGrpcMsgClient in the ClientFactory, which you can then use to query multiple modules from a single object, if you need an example with scaffold and broadcast msg you can refer to the example below in grpc-gateway:

import { osmosis } from './codegen';

const main = async () => {
  const client = await osmosis.ClientFactory.createGrpcWebClient({ endpoint });

  // now you can query the modules
  const pool = await client.osmosis.gamm.v1beta1.pool({ poolId: "1" });
  const balance = await client.cosmos.bank.v1beta1.allBalances({ address: 'osmo1addresshere' });
};

gRPC-gateway Client

For querying data via gRPC-web endpoints, you can use gRPC-web Clients. For a better developer experience, you can generate a factory of scoped bundles of all gRPC-web Clients with the rpcClients option.

const options: TelescopeOptions = {
  rpcClients: {
    type: 'grpc-gateway',
    enabled: true,
    camelCase: true
  }
};

This will generate helpers createGrpcGateWayClient in the ClientFactory, which you can then use to query multiple modules from a single object:

import { osmosis } from './codegen';

const main = async () => {
  // endpoint here is lcd endpoint
  const client = await osmosis.ClientFactory.createGrpcGateWayClient({ endpoint });

  // now you can query the modules
  const pool = await client.osmosis.gamm.v1beta1.pool({ poolId: "1" });
  const balance = await client.cosmos.bank.v1beta1.allBalances({ address: 'osmo1addresshere' });
};

Below will be an example of scaffold a grant Proto Msg for grpc-web and grpc-gateway and then broadcast it.

const { grant } = cosmos.authz.v1beta1.MessageComposer.withTypeUrl;
const msg = grant({
    granter: 'granter_address',
    grantee: 'grantee_address',
    grant: {
        authorization: StakeAuthorization.toProtoMsg({
        maxTokens: {
            denom: 'uosmo',
            amount: '100000000'
        },
        authorizationType: AuthorizationType.AUTHORIZATION_TYPE_DELEGATE
    }),
    expiration: new Date(Date.now() + 60 * 60 * 24 * 7)
}})

const signed_tx = await signClient.sign('granter_address', [msg], fee, 'telescope: grant', signerData);
const txRawBytes = Uint8Array.from(TxRaw.encode(signed_tx).finish());

const res = await client.cosmos.tx.v1beta1.broadcastTx({
    txBytes: txRawBytes,
    mode: BroadcastMode.BROADCAST_MODE_BLOCK
})

console.log(res);

RPC Client Classes

If you want to instantiate a single client, you can generate RPC classes with the rpcClients option;

For any module that has a Msg, Query or Service type, a

import { osmosis, cosmos } from 'osmojs';

const MsgClient = osmosis.gamm.v1beta1.MsgClientImpl;
const QueryClient = osmosis.gamm.v1beta1.QueryClientImpl;
const ServiceClient = cosmos.base.tendermint.v1beta1.ServiceClientImpl;

Here is an example of making a query if you want to use the RPC client classes manually:

import { osmosis } from "osmojs";
import { createProtobufRpcClient, QueryClient } from "@cosmjs/stargate";
import { Tendermint34Client } from "@cosmjs/tendermint-rpc";

export const main = async () => {
    const tmClient = await Tendermint34Client.connect(RPC_ENDPOINT);
    const QueryClientImpl = osmosis.gamm.v1beta1.QueryClientImpl;
    const client = new QueryClient(tmClient);
    const rpc = createProtobufRpcClient(client);
    const queryService = new QueryClientImpl(rpc);
    const pools = await queryService.pools({})
    console.log(pools);
};

main().then(() => {
    console.log('all done')
})

Instant RPC Methods

Using instantOps option to expose instant RPC methods.

For example, for this config:

{
    instantOps: [
      {
        className: "OsmosisClaim",
        include: {
          patterns: ["osmosis.**.*claim*"],
        },
      },
      {
        className: "CosmosAuthAccount",
        include: {
          patterns: [
            "cosmos.auth.**.*account*",
            "cosmos.auth.**.*Account*",
            "cosmos.gov.v1beta1.**",
          ],
        },
        nameMapping: {
          // name mapping rule for both Msg and Query methods.
          // moduleAccounts will be renamed to authModuleAccounts in generated class.
          All: {
            authModuleAccounts: "cosmos.auth.v1beta1.moduleAccounts",
          },
          // name mapping rule for Msg methods.
          Msg: {
            // deposit method under Msg will be renamed to txDeposit in generated class. While deposit method under Query will remain the same.
            txDeposit: "cosmos.gov.v1beta1.deposit",
            // Same for vote method.
            txVote: "cosmos.gov.v1beta1.vote",
          },
        },
      },
    ],
}

There'll be an extra file generated in the root folder called service-ops.ts:

export interface OsmosisClaim extends _OsmosisClaimV1beta1Queryrpc.OsmosisClaim {}
export class OsmosisClaim {
  rpc: TxRpc;
  init(rpc: TxRpc) {
    this.rpc = rpc;
    this.claimRecord = _OsmosisClaimV1beta1Queryrpc.createClientImpl(rpc).claimRecord;
    this.claimableForAction = _OsmosisClaimV1beta1Queryrpc.createClientImpl(rpc).claimableForAction;
  }
}
export interface CosmosAuthAccount extends _CosmosAuthV1beta1Queryrpc.CosmosAuthAccount, _CosmosGovV1beta1Queryrpc.CosmosAuthAccount, _CosmosGovV1beta1Txrpc.CosmosAuthAccount {}
export class CosmosAuthAccount {
  rpc: TxRpc;
  init(rpc: TxRpc) {
    this.rpc = rpc;
    this.accounts = _CosmosAuthV1beta1Queryrpc.createClientImpl(rpc).accounts;
    this.account = _CosmosAuthV1beta1Queryrpc.createClientImpl(rpc).account;

    // moduleAccounts has been renamed to authModuleAccounts as the nameMapping in settings.
    this.authModuleAccounts = _CosmosAuthV1beta1Queryrpc.createClientImpl(rpc).moduleAccounts;

    this.proposal = _CosmosGovV1beta1Queryrpc.createClientImpl(rpc).proposal;
    this.proposals = _CosmosGovV1beta1Queryrpc.createClientImpl(rpc).proposals;

    // vote under Query remains the same.
    this.vote = _CosmosGovV1beta1Queryrpc.createClientImpl(rpc).vote;

    this.votes = _CosmosGovV1beta1Queryrpc.createClientImpl(rpc).votes;
    this.params = _CosmosGovV1beta1Queryrpc.createClientImpl(rpc).params;

    // deposit under Query remains the same.
    this.deposit = _CosmosGovV1beta1Queryrpc.createClientImpl(rpc).deposit;

    this.deposits = _CosmosGovV1beta1Queryrpc.createClientImpl(rpc).deposits;
    this.tallyResult = _CosmosGovV1beta1Queryrpc.createClientImpl(rpc).tallyResult;
    this.submitProposal = _CosmosGovV1beta1Txrpc.createClientImpl(rpc).submitProposal;

    //same as txDeposite for vote here.
    this.txVote = _CosmosGovV1beta1Txrpc.createClientImpl(rpc).vote;

    this.voteWeighted = _CosmosGovV1beta1Txrpc.createClientImpl(rpc).voteWeighted;

    // deposit method under Msg will be renamed to txDeposit in generated class. While deposit method under Query will remain the same.
    this.txDeposit = _CosmosGovV1beta1Txrpc.createClientImpl(rpc).deposit;
  }
}

Manually registering types

This example is with osmosis module in osmojs, but it is the same pattern for any module.

NOTE: this is using @cosmjs/[email protected]

import {
    AminoTypes,
    SigningStargateClient
} from '@cosmjs/stargate';
import { Registry } from '@cosmjs/proto-signing';
import { defaultRegistryTypes } from '@cosmjs/stargate';
import { OfflineSigner } from '@cosmjs/proto-signing'
import { osmosis } from 'osmojs';

export const getCustomSigningClient = async ({ rpcEndpoint, signer }: { rpcEndpoint: string, signer: OfflineSigner }) => {
  // registry
  const registry = new Registry(defaultRegistryTypes);

  // aminotypes
  const aminoTypes = new AminoTypes({
    ...osmosis.gamm.v1beta1.AminoConverter,
    ...osmosis.lockup.AminoConverter,
    ...osmosis.superfluid.AminoConverter
  });

  // load the
  osmosis.gamm.v1beta1.load(registry);
  osmosis.lockup.load(registry);
  osmosis.superfluid.load(registry);

  const client = await SigningStargateClient.connectWithSigner(
    rpcEndpoint,
    signer,
    { registry, aminoTypes }
  );

  return client;
};

JSON Patch Protos

The prototypes.patch configuration within the options object allows for dynamic modifications to protobuf definitions during code generation. This feature is designed to apply specific changes to proto files without altering the original source. By using JSON Patch operations such as replace and add, developers can customize the generated output to better fit project requirements when upstream SDK PRs are lagging or not in production.

Patches are specified as arrays of Operations, where each operation is defined by:

  • op: The operation type (add or replace).
  • path: The JSON path to the target field, optionally prefixed with @ to denote paths derived automatically from the package name, simplifying navigation within the proto file's structure.
  • value: The new value to be set at the target location specified by the path.

Here is how these patches can be defined within the prototypes configuration:

{
    "prototypes": {
        "patch": {
            "cosmwasm/wasm/v1/types.proto": [
                {
                    "op": "replace",
                    "path": "@/AccessType/valuesOptions/ACCESS_TYPE_UNSPECIFIED/(gogoproto.enumvalue_customname)",
                    "value": "UnspecifiedAccess"
                },
                {
                    "op": "replace",
                    "path": "@/AccessType/valuesOptions/ACCESS_TYPE_NOBODY/(gogoproto.enumvalue_customname)",
                    "value": "NobodyAccess"
                },
                {
                    "op": "add",
                    "path": "@/AccessType/values/ACCESS_TYPE_SUPER_FUN",
                    "value": 4
                },
                {
                    "op": "add",
                    "path": "@/AccessType/valuesOptions/ACCESS_TYPE_SUPER_FUN",
                    "value": {
                        "(gogoproto.enumvalue_customname)": "SuperFunAccessType"
                    }
                }
            ]
        }
    }
}

CosmWasm

Generate TypeScript SDKs for your CosmWasm smart contracts by using the cosmwasm option on TelescopeOptions. The cosmwasm option is actually a direct reference to the TSBuilderInput object, for the most up-to-date documentation, visit @cosmwasm/ts-codegen.

import { TSBuilderInput } from '@cosmwasm/ts-codegen';
const options: TelescopeOptions = {
  cosmwasm: {
    contracts: [
      {
        name: 'SG721',
        dir: './path/to/sg721/schema'
      },
      {
        name: 'Minter',
        dir: './path/to/Minter/schema'
      }
    ],
    outPath: './path/to/code/src/'
  }
};

Dependencies

If you don't use the boilerplate, you will need to manually install

  • @cosmjs/amino
  • @cosmjs/proto-signing
  • @cosmjs/stargate
  • @cosmjs/tendermint-rpc
yarn add @cosmjs/amino @cosmjs/proto-signing @cosmjs/stargate @cosmjs/tendermint-rpc

If you use the LCD Client generation, you'll need to add

  • @cosmology/lcd
yarn add @cosmology/lcd

Troubleshooting

Create React App

CRA requires that you update Webpack configurations:

https://github.com/cosmos/cosmjs/blob/656e02374898afe755e980e93390591b4b65fd86/README.md#webpack-configs

Here is an example of a config-overrides.js:

https://github.com/pyramation/osmosis-ui/blob/main/config-overrides.js

Babel

This should not be an issue, but if you experience problems with syntax or are not using preset-env, you may need these babel plugins:

Developing

See our documentation for how to contribute and develop Telescope.

Sponsors

Kudos to our sponsors:

  • Osmosis funded the creation of Telescope.

Related

Checkout these related projects:

  • @cosmology/telescope Your Frontend Companion for Building with TypeScript with Cosmos SDK Modules.
  • @cosmwasm/ts-codegen Convert your CosmWasm smart contracts into dev-friendly TypeScript classes.
  • chain-registry Everything from token symbols, logos, and IBC denominations for all assets you want to support in your application.
  • cosmos-kit Experience the convenience of connecting with a variety of web3 wallets through a single, streamlined interface.
  • create-cosmos-app Set up a modern Cosmos app by running one command.
  • interchain-ui The Interchain Design System, empowering developers with a flexible, easy-to-use UI kit.
  • starship Unified Testing and Development for the Interchain.

Credits

🛠 Built by Cosmology — if you like our tools, please consider delegating to our validator ⚛️

Thanks to these engineers, teams and projects for inspiring Telescope:

Disclaimer

AS DESCRIBED IN THE TELESCOPE LICENSES, THE SOFTWARE IS PROVIDED “AS IS”, AT YOUR OWN RISK, AND WITHOUT WARRANTIES OF ANY KIND.

No developer or entity involved in creating Telescope will be liable for any claims or damages whatsoever associated with your use, inability to use, or your interaction with other users of the Telescope code or Telescope CLI, including any direct, indirect, incidental, special, exemplary, punitive or consequential damages, or loss of profits, cryptocurrencies, tokens, or anything else of value.

telescope's People

Contributors

anmol1696 avatar daniel-farina avatar dependabot[bot] avatar faddat avatar hoangdv2429 avatar junhoyeo avatar liangping avatar noahsaso avatar omahs avatar pitasi avatar pyramation avatar pysel avatar rasoulmrz avatar turadg avatar zetazzz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

telescope's Issues

final passes for initial open-source

  • imports not correct MetricDescriptor

Looks like we're not applying scopes to names.

  • unsupported Service type. Contact Maintainers. inside parse.ts

importing enums and mapentry names

  • metricDescriptor_MetricKindFromJSON became metricDescriptorMetricKindToJSON

from partial

  • MetricDescriptorMetadata should be MetricDescriptor_MetricDescriptorMetadata

hash map defaults

  • Metric_LabelsMapEntry

missing MapEntries

  • Metric_LabelsMapEntry vs Metric_StringMapEntry

hash map fromPartial, toJSON, fromJSON methods

  • quota.ts

Empty Files

  • annotations.ts (google)

CheckedExpr interface (keyType)

  • CheckedExpr

CheckedExpr_ReferenceMapMapEntry vs CheckedExpr_ReferenceMapEntry

  • CheckedExpr_ReferenceMapMapEntry

Decl_FunctionDecl_Overload

  • Decl_FunctionDecl_Overload

struct wrap and unwrap

Things that use Struct have special handling

// from ts-proto codebase
 const unwrap = (decodedValue: any): Code => {
        if (isListValueType(field) || isStructType(field) || isAnyValueType(field) || isFieldMaskType(field)) {
          return code`${type}.unwrap(${decodedValue})`;
        }
        return code`${decodedValue}.value`;
      };

https://github.com/pyramation/protobuf-example-reference/blob/master/ts/google/api/servicecontrol/v1/log_entry.ts#L205

  wrap(object: {
    [key: string]: any;
  } | undefined): Struct {
    const struct = createBaseStruct();

    if (object !== undefined) {
      Object.keys(object).forEach(key => {
        struct.fields[key] = object[key];
      });
    }

    return struct;
  },

  unwrap(message: Struct): {
    [key: string]: any;
  } {
    const object: {
      [key: string]: any;
    } = {};
    Object.keys(message.fields).forEach(key => {
      object[key] = message.fields[key];
    });
    return object;
  }

duration type for `MsgLockTokens` not compatible with latest `ts-proto` output

messages that are successful in the API look like this, where the duration is basically a string:

{
      "type": "osmosis/lockup/lock-tokens",
      "value": {
        "owner": "osmo1g5555555555555555555555",
        "duration": "1209600000000000",
        "coins": [
          {
            "amount": "367423715380310220",
            "denom": "gamm/pool/1"
          }
        ]
      }
    }

However, the go repository refers to a google duration, which in turn looks like

{
   seconds: '1209600', // technically of type "Long"
   nanos: 0
}

the problem

The ts-proto output for MsgLockTokens.fromPartial() results in this nested property { seconds, nanos}, not the string that the API expects.

possible scope bug

package: google/api/expr/v1alpha1/checked
Type: IdentDecl
prop: value

issue:

"value": {
    "id": 2,
    "import": "google/api/expr/v1alpha1/syntax.proto",
    "importedName": undefined,
    "parsedType": {
        "name": "Constant",
        "type": "Type",
    },
    "scope": [
        [
            "google.api.expr.v1alpha1",
        ],
    ],
    "scopeType": "import",
    "type": "Constant",
}

NOTE: this is possibily because it's used on options and nothing else. We can verify this.

use transpilation to automate some or all of the `toAmino`/`fromAmino` conversions

Testing and Documentation

Todo

  • - [ ] TDD development for all modules
  • Fixtures for all necessary endpoints
  • Docker image with local testing
  • local version of osmosis, with pools, etc.
  • Boilerplate for testing
  • API docs

Example Dapps

  • a webpage with LBP for a token launch
  • a webpage that allows users to lock tokens into a liquidity pool
  • a webpage with token swap to allow users to buy token

research simpler protobufjs style

Akashlytics takes an interesting, very minimal approach: https://github.com/Akashlytics/akashlytics-deploy/blob/master/src/shared/protoTypes/v1beta2.js

import { Field, Enum, Type, Root } from "protobufjs";

// Deployments
const DeploymentID = new Type("DeploymentID").add(new Field("owner", 1, "string")).add(new Field("dseq", 2, "uint64"));
const Coin = new Type("Coin").add(new Field("denom", 1, "string")).add(new Field("amount", 2, "string"));
const Kind = new Enum("Kind", { SHARED_HTTP: 0, RANDOM_PORT: 1 });
const Endpoint = new Type("Endpoint").add(new Field("kind", 1, "Kind")).add(Kind).add(new Field("sequence_number", 2, "uint32"));
const Attribute = new Type("Attribute").add(new Field("key", 1, "string")).add(new Field("value", 2, "string"));
const ResourceValue = new Type("ResourceValue").add(new Field("val", 1, "string"));
const CPU = new Type("CPU")
  .add(new Field("units", 1, "ResourceValue"))
  .add(ResourceValue)
  .add(new Field("attributes", 2, "Attribute", "repeated"))
  .add(Attribute);
const Memory = new Type("Memory")
  .add(new Field("quantity", 1, "ResourceValue"))
  .add(ResourceValue)
  .add(new Field("attributes", 2, "Attribute", "repeated"))
  .add(Attribute);
const Storage = new Type("Storage")
  .add(new Field("name", 1, "string"))
  .add(new Field("quantity", 2, "ResourceValue"))
  .add(ResourceValue)
  .add(new Field("attributes", 3, "Attribute", "repeated"))
  .add(Attribute);
const ResourceUnits = new Type("ResourceUnits")
  .add(new Field("cpu", 1, "CPU"))
  .add(CPU)
  .add(new Field("memory", 2, "Memory"))
  .add(Memory)
  .add(new Field("storage", 3, "Storage", "repeated"))
  .add(Storage)
  .add(new Field("endpoints", 4, "Endpoint", "repeated"))
  .add(Endpoint);

proper bundling of the proto packages as typescript namespaces

Before keplr moved to ts-proto they were using a namespaced output, which looked something like:'

export namespace cosmos {
	/** Namespace base. */
	namespace base {
		/** Namespace v1beta1. */
		namespace v1beta1 {
			/** Properties of a Coin. */
			interface ICoin {
				/** Coin denom */
				denom?: string | null;

				/** Coin amount */
				amount?: string | null;
			}

			/** Represents a Coin. */
			class Coin implements ICoin {
                         }
                 }
         }
}

https://github.com/chainapsis/keplr-wallet/blob/d6baaddd6398e906852478007821f5f53659cae1/packages/cosmos/src/stargate/proto/generated/codecimpl.d.ts#L3839-L3851
chainapsis/keplr-wallet@dd9f9b0#commitcomment-70544047
https://github.com/osmosis-labs/osmosis-frontend/blob/master/src/proto/generated/codecimpl.d.ts#L3-L18

ts-proto doesn't output namespaces, and if we can, they provide a nice interface to export all packages without conflicts, as well as provide good developer experience.

Data Layer and Message Composer

  • Compose messages for signing
  • Calculating swaps
  • Pool routing
  • Automate a series of messages
  • Higher-level compositions, e.g. allow users to specify an investment strategy

AminoConverters may need to be class with prefix

  • research prefix below

https://github.com/cosmos/cosmjs/blob/fb5cc27c3b53b8b4c8e1e003bb8b01c103b47bb9/packages/stargate/src/modules/staking/aminomessages.ts#L185-L190

https://github.com/cosmos/cosmjs/blob/fb5cc27c3b53b8b4c8e1e003bb8b01c103b47bb9/packages/stargate/src/modules/staking/aminomessages.ts#L122-L123

function toAmino() {
    return {
        pubkey: {
            typeUrl: "/cosmos.crypto.secp256k1.PubKey",
            value: fromBase64(decodeBech32Pubkey(pubkey).value),
        }
    }

}

function fromAmino() {
    return {
        pubkey: encodeBech32Pubkey(
            {
                type: "tendermint/PubKeySecp256k1",
                value: toBase64(pubkey.value),
            },
            prefix,
        )
    }
}

TypeScript Library

  • Core Telescope Module
  • Osmosis Telescope Module
    • Implement osmosis-related protobuf messages
    • #95
    • #15
  • Registry System
    • Loading channels and IBC Denominations for networks
    • Canonicalizing access symbols and denominations, and exponents
  • Agnostic Signing
    • Will be designed to sign transactions with keplr or cosmjs
  • Interactive CLI

investigate if we can use the reflection API

https://github.com/cosmos/cosmos-sdk/blob/master/proto/cosmos/base/reflection/v2alpha1/reflection.proto#L115

@ValarDragon do you know if this would be available via the LCD REST endpoints or GRPC?

Specifically, looks like /cosmos/base/reflection/v1beta1/app_descriptor/tx_descriptor would be one to try

So far I've tried all of these with no luck:

https://lcd-osmosis.blockapsis.com/cosmos/base/reflection/v1beta1/app_descriptor/tx_descriptor
https://rest-osmosis.ecostake.com/cosmos/base/reflection/v1beta1/app_descriptor/tx_descriptor

byte functions for encode/decode/tojson/fromjson

This code needs to be injected when bytes are used

https://github.com/pyramation/protobuf-example-reference/blob/master/ts/cosmwasm/wasm/v1/query.ts#L1483-L1520

declare var self: any | undefined;
declare var window: any | undefined;
declare var global: any | undefined;

var globalThis: any = (() => {
  if (typeof globalThis !== "undefined") return globalThis;
  if (typeof self !== "undefined") return self;
  if (typeof window !== "undefined") return window;
  if (typeof global !== "undefined") return global;
  throw "Unable to locate global object";
})();

const atob: (b64: string) => string = globalThis.atob || (b64 => globalThis.Buffer.from(b64, "base64").toString("binary"));

function bytesFromBase64(b64: string): Uint8Array {
  const bin = atob(b64);
  const arr = new Uint8Array(bin.length);

  for (let i = 0; i < bin.length; ++i) {
    arr[i] = bin.charCodeAt(i);
  }

  return arr;
}

const btoa: (bin: string) => string = globalThis.btoa || (bin => globalThis.Buffer.from(bin, "binary").toString("base64"));

function base64FromBytes(arr: Uint8Array): string {
  const bin: string[] = [];

  for (const byte of arr) {
    bin.push(String.fromCharCode(byte));
  }

  return btoa(bin.join(""));
}

is there a convention for typeUrl => aminoType names?

typeUrl and aminoType

  • is there some convention to go between the typeUrl and aminoType?

e.g.

typeUrl: /cosmos.staking.v1beta1.MsgCreateValidator => aminoType: cosmos-sdk/MsgCreateValidator

typeUrl: /osmosis.gamm.v1beta1.MsgJoinSwapExternAmountIn => aminoType: osmosis/gamm/join-swap-extern-amount-in

serialization of `Long` type

Seems that we need Long type to serialize to a string to correctly pass through the toAmino/fromAmino inside of stargate.sign

correct

"poolId": "4"

incorrect

"poolId": {
    "low": 4,
    "high": 0,
    "unsigned": false
}

relates to #21

design buy token ui

Modify current swap UI to transform it into a single asset purchase process. This UI will allow users to select a coin and then click a button to buy it.

`Decl` interface

export interface Decl {
  /**
   * The fully qualified name of the declaration.
   *
   * Declarations are organized in containers and this represents the full path
   * to the declaration in its container, as in `google.api.expr.Decl`.
   *
   * Declarations used as [FunctionDecl.Overload][google.api.expr.v1alpha1.Decl.FunctionDecl.Overload] parameters may or may not
   * have a name depending on whether the overload is function declaration or a
   * function definition containing a result [Expr][google.api.expr.v1alpha1.Expr].
   */
  name: string;
  /** Identifier declaration. */

  ident: Decl_IdentDecl | undefined;
  /** Function declaration. */

  function: Decl_FunctionDecl | undefined;
}

ensure to export declaration files for TS after babel transpilation

https://www.typescriptlang.org/docs/handbook/babel-with-typescript.html

"compilerOptions": {
  // Ensure that .d.ts files are created by tsc, but not .js files
  "[declaration](https://www.typescriptlang.org/tsconfig#declaration)": true,
  "[emitDeclarationOnly](https://www.typescriptlang.org/tsconfig#emitDeclarationOnly)": true,
  // Ensure that Babel can safely transpile files in the TypeScript project
  "[isolatedModules](https://www.typescriptlang.org/tsconfig#isolatedModules)": true
}

it's possible we'll need run 3 processes

  1. ts-proto
  2. babel transpile
  3. TS declaration generation

https://v4.chriskrycho.com/2018/how-to-bundle-typescript-type-definitions.html

  1. properly link the typings

https://medium.com/jspoint/typescript-type-declaration-files-4b29077c43
https://stackoverflow.com/questions/48952313/what-to-put-in-package-json-types-field-for-typescript-based-libs

new amino registry not correct for osmosis gamm v1beta

to/from amino with latest code doesn't serialize correctly — I'm testing out the latest with the prototype and getting serialization errors:

Error: Broadcasting transaction failed with code 4 (codespace: sdk). Log: signature verification failed; please verify account number (441730), sequence (133) and chain-id (osmosis-1): unauthorized
    at SigningStargateClient.broadcastTx
  • determine how to properly manage the Long type when serializing

use implicit recursion similar to fromPartial for toAmino/fromAmino

currently the toAmino/fromAmino functionality requires the proto parser store and proto refs so that it can query information about nested dependencies and imports.

This could be simplified by taking an approach for amino encoding similar to how fromPartial, fromJSON, toJSON and other methods inspired from ts-proto

This could be accomplished by adding toAminoandfromAmino` methods to the message objects:

export const MsgCreateValidator = {
  encode(message: MsgCreateValidator, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer {
    if (message.description !== undefined) {
      Description.encode(message.description, writer.uint32(10).fork()).ldelim();
    }
    if (message.commission !== undefined) {
      CommissionRates.encode(message.commission, writer.uint32(18).fork()).ldelim();
    }
  }
  toAmino(message, obj) {
      if (message.description !== undefined) {
         obj.description = Description.toAmino(message.description);
     }
  }

App Boilerplate Generator

This gives users “rails” they need for the correct JS architecture to build apps fast and correctly.

  • user can set up a modern web app, wired up with Telescope
    • api clients + local state
  • ready to build out-of-the-box with pre-built components
    • connect wallet
    • staking
    • themeable view components
  • Registry
    • integration with cosmos-registry
    • registry for pluggable cosmos chain telescope modules, including:
      • wallets
      • queries
      • messages
      • IBC channels, denominations
  • create-cosmos-app
    • CLI for generating boilerplates
  • Integrations

[EPIC] simplify Amino/Protobuf hurdles, making it easy to craft transactions as a client-side developer.

Sprint 1

In regards to the prototype https://github.com/pyramation/osmosis-protobufs when running files as the input, getting this error, so I instead created a for loop and am generating the types for each proto file individually. filed issue here osmosis-labs/osmosis#1185

osmosis/gamm/v1beta1/tx.proto:9:9: "osmosis.gamm.v1beta1.Msg" is already defined in file "osmosis/gamm/pool-models/balancer/tx.proto"
  • missing protobuf references

some protobuf references version don’t exist (e.g. gogo/protobuf#737 ) so I found them here https://github.com/regen-network/protobuf

  • use babel to transpile the result and make better TS objects

https://github.com/cosmology-finance/cosmology/blob/master/packages/protobufs/src/proto/osmosis/gamm/v1beta1/tx.ts#L1560-L1563

https://github.com/cosmology-finance/cosmology/blob/master/packages/protobufs/bin/gen-proto.js

Backlog

check default values for encode/decode

I've seen some with 0, Long.UZERO, but now some that are undefined.

      nullValue: isSet(object.nullValue) ? nullValueFromJSON(object.nullValue) : 0,
      boolValue: isSet(object.boolValue) ? Boolean(object.boolValue) : false,
      int64Value: isSet(object.int64Value) ? Long.fromString(object.int64Value) : undefined,
      uint64Value: isSet(object.uint64Value) ? Long.fromString(object.uint64Value) : Long.UZERO,
      doubleValue: isSet(object.doubleValue) ? Number(object.doubleValue) : undefined,
      stringValue: isSet(object.stringValue) ? String(object.stringValue) : \\"\\",
      bytesValue: isSet(object.bytesValue) ? bytesFromBase64(object.bytesValue) : new Uint8Array(),
      durationValue: isSet(object.durationValue) ? Duration.fromJSON(object.durationValue) : undefined,
      timestampValue: isSet(object.timestampValue) ? Timestamp.fromJSON(object.timestampValue) : undefined

both unsigned and signed zeros

  • UZERO
  • ZERO

Utilities

  • “auto-compounding” and staking automation with IBC routing
    • LP rewards to staking LP
    • LP rewards to staking coins
    • staking rewards to staking LP
    • staking rewards to staking coins
  • Authz Integration
    • Integrated with auto-compounding functionality

context imports

AminoParseContext should allow for imports to be added dynamically by field parsing.

a crude example context.imports.push('toBase64')

  1. ability to reference external libraries, e.g. AminoMsg from proto signing
  2. ability to reference helpers, e.g, toBase64, toDuration, etc.

Nested keyTypes

goal

export const CheckedExpr_TypeMapEntry = {
  encode(message: CheckedExpr_TypeMapEntry, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer {
    if (!message.key.isZero()) {
      writer.uint32(8).int64(message.key);
    }

current

 export const CheckedExpr_TypeMapEntry = {
    +   encode(message: CheckedExpr_TypeMapEntry, writer = _m0.Writer.create()): _m0.Writer {
    +     if (message.key !== undefined) {
    +       writer.uint32(8).double(message.key);
    +

npx style telescope builder without having to download

Is it possible to wrap something like this in a module?
and instead reference ./node_modules through a global npm install?

#!/bin/bash

PROTO_PATH=./proto
OUT_DIR=./out/proto

FILES=`find proto -type f -name "*.proto"`

mkdir -p ${OUT_DIR}

protoc \
    --plugin="./node_modules/.bin/protoc-gen-ts_proto" \
    --ts_proto_out="${OUT_DIR}" \
    --proto_path="${PROTO_PATH}" \
    --ts_proto_opt="esModuleInterop=true,forceLong=long,useOptionals='messages',outputClientImpl=true" \
    ${FILES}
  • check how protobuf-ts does it here
npx protoc --ts_out . --proto_path protos protos/my.proto 

determine protobufjs types -> TS types

initialization

  • arrays

https://github.com/pyramation/protobuf-example-reference/blob/master/ts/cosmos/app/v1alpha1/config.ts#L45
https://github.com/pyramation/protobuf-example-reference/blob/master/json/cosmos/app/v1alpha1/config.json#L18

imports

  • google.protobuf.Any -> Any (imported)

https://github.com/pyramation/protobuf-example-reference/blob/master/json/cosmos/app/v1alpha1/config.json#L31
https://github.com/pyramation/protobuf-example-reference/blob/master/ts/cosmos/app/v1alpha1/config.ts#L4

  • google.protobuf.Empty -> Empty (imported)
  • google.protobuf.NullValue -> NullValue (imported enum)

https://github.com/pyramation/protobuf-example-reference/blob/master/json/google/api/expr/v1alpha1/checked.json#L74-L78
https://github.com/pyramation/protobuf-example-reference/blob/master/ts/google/api/expr/v1alpha1/checked.ts#L5-L6

  • google.protobuf.Duration -> string | Duration (imported from google)

https://github.com/pyramation/protobuf-example-reference/blob/master/json/osmosis/claim/v1beta1/params.json#L32-L38
https://github.com/pyramation/protobuf-example-reference/blob/master/ts/osmosis/claim/v1beta1/params.ts#L5

Sometimes we'll also see scoped imports of "local" modules

  • cosmos.base.query.v1beta1.PageResponse -> PageResponse (imported)

https://github.com/pyramation/protobuf-example-reference/blob/master/json/cosmos/authz/v1beta1/query.json#L101
https://github.com/pyramation/protobuf-example-reference/blob/master/ts/cosmos/authz/v1beta1/query.ts#L27

Sometimes we'll see package imports, it doesn't say which file:

  • .tendermint.types.BlockID -> BlockID (imported from tendermint/types/types)
  • .tendermint.types.Block ->Block (imported from tendermint/types/block)

https://github.com/pyramation/protobuf-example-reference/blob/master/ts/cosmos/tx/v1beta1/service.ts#L7-L8
https://github.com/pyramation/protobuf-example-reference/blob/master/json/cosmos/tx/v1beta1/service.json#L238-L242

Another package ref

  • google.rpc.Status -> Status (imported from status rpc nested Status obj)

https://github.com/pyramation/protobuf-example-reference/blob/master/json/google/api/expr/v1alpha1/eval.json#L82
https://github.com/pyramation/protobuf-example-reference/blob/master/json/google/rpc/status.json#L21

Package ref (IBC)

  • ibc.core.client.v1.Height -> Height (imported from ibc/core/client/v1/client)

https://github.com/pyramation/protobuf-example-reference/blob/master/json/ibc/core/channel/v1/query.json#L234
https://github.com/pyramation/protobuf-example-reference/blob/master/ts/ibc/core/channel/v1/query.ts#L5

imports only for meta info

"gogoproto/gogo.proto"

https://github.com/pyramation/protobuf-example-reference/blob/master/json/cosmos/base/abci/v1beta1/abci.json#L4
https://github.com/pyramation/protobuf-example-reference/blob/master/json/cosmos/base/abci/v1beta1/abci.json#L20

oneofs

https://github.com/pyramation/protobuf-example-reference/blob/master/json/confio/proofs.json#L71-L99
https://github.com/pyramation/protobuf-example-reference/blob/master/ts/confio/proofs.ts#L227-L232

Validator to StakeAuthorization_Validators

https://github.com/pyramation/protobuf-example-reference/blob/master/ts/cosmos/staking/v1beta1/authz.ts#L84-L87
https://github.com/pyramation/protobuf-example-reference/blob/master/json/cosmos/staking/v1beta1/authz.json#L24-L30

because it used a nested definition: https://github.com/pyramation/protobuf-example-reference/blob/master/json/cosmos/staking/v1beta1/authz.json#L54

more examples:
https://github.com/pyramation/protobuf-example-reference/blob/master/ts/cosmos/tx/signing/v1beta1/signing.ts#L130-L154
https://github.com/pyramation/protobuf-example-reference/blob/master/json/cosmos/tx/signing/v1beta1/signing.json#L55-L98

scope lookup

When looking for a symbol

  1. potentially, only when using oneof seems, check a nested https://github.com/pyramation/protobuf-example-reference/blob/master/json/cosmos/staking/v1beta1/authz.json#L54
  2. look in existing file
  3. look in the imports

validations

Cosmos Addresses

https://github.com/pyramation/protobuf-example-reference/blob/master/json/cosmos/staking/v1beta1/tx.json#L78-L91

Numbers that are stored as strings

https://github.com/pyramation/protobuf-example-reference/blob/master/ts/cosmos/staking/v1beta1/tx.ts#L34-L35
https://github.com/pyramation/protobuf-example-reference/blob/master/json/cosmos/staking/v1beta1/tx.json#L132-L146

object keys (keyType)

https://github.com/pyramation/protobuf-example-reference/blob/master/json/google/api/expr/v1alpha1/checked.json#L29
https://github.com/pyramation/protobuf-example-reference/blob/master/ts/google/api/expr/v1alpha1/checked.ts#L28

then to/from json

https://github.com/pyramation/protobuf-example-reference/blob/master/ts/google/api/expr/v1alpha1/checked.ts#L594-L598
https://github.com/pyramation/protobuf-example-reference/blob/master/ts/google/api/expr/v1alpha1/checked.ts#L616-L620

enums

when referenced, just like other objects that are imported

https://github.com/pyramation/protobuf-example-reference/blob/master/json/cosmos/tx/v1beta1/tx.json#L273
https://github.com/pyramation/protobuf-example-reference/blob/master/ts/cosmos/tx/v1beta1/tx.ts#L311

but then using function with to/from json: https://github.com/pyramation/protobuf-example-reference/blob/master/ts/cosmos/tx/v1beta1/tx.ts#L1430-L1446

Can also be nested

https://github.com/pyramation/protobuf-example-reference/blob/master/ts/google/api/expr/conformance/v1alpha1/conformance_service.ts#L123

https://github.com/pyramation/protobuf-example-reference/blob/master/json/google/api/expr/conformance/v1alpha1/conformance_service.json#L173-L179

strings

string->string

numbers

  • int32 -> number

https://github.com/pyramation/protobuf-example-reference/blob/master/json/confio/proofs.json#L151
https://github.com/pyramation/protobuf-example-reference/blob/master/ts/confio/proofs.ts#L307

  • uint32 -> number

https://github.com/pyramation/protobuf-example-reference/blob/master/json/cosmos/app/v1alpha1/module.json#L44-L46
https://github.com/pyramation/protobuf-example-reference/blob/master/ts/cosmos/app/v1alpha1/module.ts#L78

  • int32 (repeated) -> number[]

https://github.com/pyramation/protobuf-example-reference/blob/master/json/confio/proofs.json#L163-L166
https://github.com/pyramation/protobuf-example-reference/blob/master/ts/confio/proofs.ts#L329

  • int64 -> Long

https://github.com/pyramation/protobuf-example-reference/blob/master/json/cosmos/base/abci/v1beta1/abci.json#L29
https://github.com/pyramation/protobuf-example-reference/blob/master/ts/cosmos/base/abci/v1beta1/abci.ts#L13

  • uint64 -> Long

https://github.com/pyramation/protobuf-example-reference/blob/master/ts/cosmos/auth/v1beta1/auth.ts#L14-L15
https://github.com/pyramation/protobuf-example-reference/blob/master/json/cosmos/auth/v1beta1/auth.json#L42-L49

  • bytes -> Uint8Array

https://github.com/pyramation/protobuf-example-reference/blob/master/ts/confio/proofs.ts#L227-L232
https://github.com/pyramation/protobuf-example-reference/blob/master/ts/confio/proofs.ts#L208

booleans

  • bool -> boolean

https://github.com/pyramation/protobuf-example-reference/blob/master/ts/cosmos/bank/v1beta1/bank.ts#L9
https://github.com/pyramation/protobuf-example-reference/blob/master/json/cosmos/bank/v1beta1/bank.json#L32

date and time

  • google.protobuf.Timestamp -> Date

https://github.com/pyramation/protobuf-example-reference/blob/master/ts/cosmos/authz/v1beta1/authz.ts#L30
https://github.com/pyramation/protobuf-example-reference/blob/master/json/cosmos/authz/v1beta1/authz.json#L47

amino serialization relations

  • pubkey

https://github.com/pyramation/protobuf-example-reference/blob/master/json/cosmos/staking/v1beta1/tx.json#L92-L98
https://github.com/cosmos/cosmjs/blob/main/packages/stargate/src/modules/staking/aminomessages.ts#L224-L227

weird corner cases

keyMap + referencing a type that is a oneof:

https://github.com/pyramation/protobuf-example-reference/blob/master/json/google/protobuf/struct.json#L22
https://github.com/pyramation/protobuf-example-reference/blob/master/ts/google/protobuf/struct.ts#L51

and then, fields.fields get's unnested when a child refs the parent?

https://github.com/pyramation/protobuf-example-reference/blob/master/json/google/protobuf/struct.json#L20-L21
https://github.com/pyramation/protobuf-example-reference/blob/master/ts/google/protobuf/struct.ts#L81-L83
https://github.com/pyramation/protobuf-example-reference/blob/master/json/google/protobuf/struct.json#L58-L60

exceptions or not supported by ts-proto

Single field extending an object

https://github.com/osmosis-labs/telescope/blob/master/packages/osmosis/proto/cosmos/msg/v1/msg.proto
https://github.com/pyramation/protobuf-example-reference/blob/master/ts/cosmos/msg/v1/msg.ts
https://github.com/pyramation/protobuf-example-reference/blob/master/json/cosmos/msg/v1/msg.json

Lower case names seemingly not implemented

https://github.com/pyramation/protobuf-example-reference/blob/master/json/cosmos_proto/cosmos.json#L14-L42
https://github.com/pyramation/protobuf-example-reference/blob/master/ts/cosmos_proto/cosmos.ts#L50

castrepeated (for arrays being cast)

https://github.com/pyramation/protobuf-example-reference/blob/master/ts/cosmos/bank/v1beta1/authz.ts#L13
https://github.com/pyramation/protobuf-example-reference/blob/master/json/cosmos/bank/v1beta1/authz.json#L31

"(gogoproto.nullable)": true

https://github.com/pyramation/protobuf-example-reference/blob/master/ts/cosmos/authz/v1beta1/authz.ts#L30
https://github.com/pyramation/protobuf-example-reference/blob/master/json/cosmos/authz/v1beta1/authz.json#L47

omitempty jsontag https://www.sohamkamani.com/golang/omitempty/

gogoproto.customname is also ignored, but could be used to change the name of a field

gogoproto.moretags seems to be ignored

"(gogoproto.casttype)": "RawContractMessage"

Structs

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.