Skip to content

Commit f06682e

Browse files
authored
Merge pull request #1933 from cosmos/proto-signing-types
Add support for ts-proto version 2
2 parents 8184348 + fdfedf9 commit f06682e

File tree

10 files changed

+267
-22
lines changed

10 files changed

+267
-22
lines changed

.pnp.cjs

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:8fb0c728d64f4a31ee6063d605fd769a883d8e2bd9a4d7cc7790681016048642
3+
size 1655274

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,12 @@ and this project adheres to
2424
- @cosmjs/cosmwasm-stargate: Add the ability to specify a custom account parser
2525
for `CosmWasmClient`. ([#1928])
2626
- Add support for Cosmos EVM key handling and signing. ([#1932])
27+
- @cosmjs/proto-signing: Add support for ts-proto v2 through the newly added
28+
`TsProto2GeneratedType` interface. As long as the existing
29+
`TsProtoGeneratedType` is not removed, ts-proto v1 remains supported.
30+
([#1613])
2731

32+
[#1613]: https://github.com/cosmos/cosmjs/issues/1613
2833
[#1883]: https://github.com/cosmos/cosmjs/issues/1883
2934
[#1916]: https://github.com/cosmos/cosmjs/pull/1916
3035
[#1926]: https://github.com/cosmos/cosmjs/pull/1926
@@ -114,6 +119,13 @@ and this project adheres to
114119
through strings in cases where you have a BitInt already. Strings remain
115120
supported for convenient usage with coins.
116121
- @cosmjs/math: `Decimal` now supports negative values. ([#1930])
122+
- @cosmjs/proto-signing: Remove `isTelescopeGeneratedType`,
123+
`isTsProtoGeneratedType` and `isPbjsGeneratedType` because they are
124+
unreliable. E.g. the `typeUrl` in Telescope may or may not exist depending on
125+
the configuration. The newly added `hasFromPartial`/`hasCreate` allow you to
126+
check for `TelescopeGeneratedType | TsProtoGeneratedType`/`PbjsGeneratedType`
127+
such that you can create instanes through
128+
`MyMessage.fromPartial()`/`MyMessage.create()`.
117129

118130
[#1883]: https://github.com/cosmos/cosmjs/issues/1883
119131
[#1866]: https://github.com/cosmos/cosmjs/issues/1866

packages/proto-signing/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
"cosmjs-types": "^0.11.0"
5050
},
5151
"devDependencies": {
52+
"@bufbuild/protobuf": "^2.10.2",
5253
"@istanbuljs/nyc-config-typescript": "^1.0.1",
5354
"@types/jasmine": "^4",
5455
"@types/karma-firefox-launcher": "^2",

packages/proto-signing/src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,11 @@ export type {
1616
EncodeObject,
1717
GeneratedType,
1818
PbjsGeneratedType,
19+
TsProto2GeneratedType,
1920
TsProtoGeneratedType,
2021
TxBodyEncodeObject,
2122
} from "./registry";
22-
export { isPbjsGeneratedType, isTsProtoGeneratedType, isTxBodyEncodeObject, Registry } from "./registry";
23+
export { hasCreate, hasFromPartial, isTxBodyEncodeObject, Registry } from "./registry";
2324
export type { AccountData, Algo, DirectSignResponse, OfflineDirectSigner, OfflineSigner } from "./signer";
2425
export { isOfflineDirectSigner } from "./signer";
2526
export { makeAuthInfoBytes, makeSignBytes, makeSignDoc } from "./signing";

packages/proto-signing/src/registry.spec.ts

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,47 @@ import { TxBody } from "cosmjs-types/cosmos/tx/v1beta1/tx";
66
import { Any } from "cosmjs-types/google/protobuf/any";
77
import { Field, Type } from "protobufjs";
88

9-
import { isPbjsGeneratedType, isTsProtoGeneratedType, Registry } from "./registry";
9+
import {
10+
hasCreate,
11+
hasFromPartial,
12+
Registry,
13+
TelescopeGeneratedType,
14+
TsProto2GeneratedType,
15+
TsProtoGeneratedType,
16+
} from "./registry";
17+
import * as tsProto1 from "./testdata/ts-proto-v1";
18+
import * as tsProto2 from "./testdata/ts-proto-v2";
1019

1120
describe("registry demo", () => {
21+
describe("TelescopeGeneratedType", () => {
22+
it("is compatible with cosmjs-types generated types", () => {
23+
const t: TelescopeGeneratedType = TxBody;
24+
expect(typeof t.fromPartial).toEqual("function");
25+
});
26+
});
27+
28+
describe("TsProtoGeneratedType", () => {
29+
it("is compatible with ts-proto v1 generated types", () => {
30+
const t: TsProtoGeneratedType = tsProto1.Record;
31+
expect(typeof t.fromPartial).toEqual("function");
32+
});
33+
});
34+
35+
describe("TsProto2GeneratedType", () => {
36+
it("is compatible with ts-proto v2 generated types", () => {
37+
const t: TsProto2GeneratedType = tsProto2.Record;
38+
expect(typeof t.fromPartial).toEqual("function");
39+
});
40+
});
41+
1242
it("works with a default msg", () => {
1343
const registry = new Registry();
1444
const Coin = registry.lookupType("/cosmos.base.v1beta1.Coin");
1545
const MsgSend = registry.lookupType("/cosmos.bank.v1beta1.MsgSend");
1646
assert(Coin);
1747
assert(MsgSend);
18-
assert(isTsProtoGeneratedType(Coin));
19-
assert(isTsProtoGeneratedType(MsgSend));
48+
assert(hasFromPartial(Coin));
49+
assert(hasFromPartial(MsgSend));
2050

2151
const coin = Coin.fromPartial({
2252
denom: "ucosm",
@@ -67,7 +97,7 @@ describe("registry demo", () => {
6797
registry.register(typeUrl, MsgCreatePostOriginal);
6898
const MsgCreatePost = registry.lookupType(typeUrl);
6999
assert(MsgCreatePost);
70-
assert(isPbjsGeneratedType(MsgCreatePost));
100+
assert(hasCreate(MsgCreatePost));
71101

72102
const msgDemo = MsgCreatePost.create({
73103
creator: "Me",

packages/proto-signing/src/registry.ts

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type * as bbw from "@bufbuild/protobuf/wire";
12
import { fixUint8Array } from "@cosmjs/encoding";
23
import { BinaryWriter } from "cosmjs-types/binary";
34
import { MsgSend } from "cosmjs-types/cosmos/bank/v1beta1/tx";
@@ -25,7 +26,7 @@ export interface TelescopeGeneratedType {
2526
}
2627

2728
/**
28-
* A type generated by [ts-proto](https://github.com/stephenh/ts-proto).
29+
* A type generated by [ts-proto](https://github.com/stephenh/ts-proto) version 1.
2930
*/
3031
export interface TsProtoGeneratedType {
3132
readonly encode: (message: any | { [k: string]: any }, writer?: protobuf.Writer) => protobuf.Writer;
@@ -34,6 +35,29 @@ export interface TsProtoGeneratedType {
3435
// Methods from ts-proto types we don't need
3536
// readonly fromJSON: (object: any) => any;
3637
// readonly toJSON: (message: any | { [k: string]: any }) => unknown;
38+
39+
// This was added at some point in 2023 and looks pretty compatible to the PbjsGeneratedType constructor.
40+
// but we don't need it as long as Telescope does not have it. `fromPartial` is widespread and works well.
41+
// See https://github.com/stephenh/ts-proto/pull/760
42+
// readonly create: (object?: any) => any;
43+
}
44+
45+
/**
46+
* A type generated by [ts-proto](https://github.com/stephenh/ts-proto) version 2.
47+
* See "ts-proto 2.x Release Notes" at https://github.com/stephenh/ts-proto/blob/v2.10.0/README.markdown#ts-proto-2x-release-notes
48+
*/
49+
export interface TsProto2GeneratedType {
50+
readonly encode: (message: any | { [k: string]: any }, writer?: bbw.BinaryWriter) => bbw.BinaryWriter;
51+
readonly decode: (input: Uint8Array | bbw.BinaryReader, length?: number) => any;
52+
readonly fromPartial: (object: any) => any;
53+
// Methods from ts-proto types we don't need
54+
// readonly fromJSON: (object: any) => any;
55+
// readonly toJSON: (message: any | { [k: string]: any }) => unknown;
56+
57+
// This was added at some point in 2023 and looks pretty compatible to the PbjsGeneratedType constructor.
58+
// but we don't need it as long as Telescope does not have it. `fromPartial` is widespread and works well.
59+
// See https://github.com/stephenh/ts-proto/pull/760
60+
// readonly create: (object?: any) => any;
3761
}
3862

3963
/**
@@ -48,19 +72,23 @@ export interface PbjsGeneratedType {
4872
readonly decode: (reader: protobuf.Reader | Uint8Array, length?: number) => any;
4973
}
5074

51-
export type GeneratedType = TelescopeGeneratedType | TsProtoGeneratedType | PbjsGeneratedType;
52-
53-
export function isTelescopeGeneratedType(type: GeneratedType): type is TelescopeGeneratedType {
54-
const casted = type as TelescopeGeneratedType;
55-
return typeof casted.fromPartial === "function" && typeof casted.typeUrl == "string";
56-
}
57-
58-
export function isTsProtoGeneratedType(type: GeneratedType): type is TsProtoGeneratedType {
59-
return typeof (type as TsProtoGeneratedType).fromPartial === "function";
75+
export type GeneratedType =
76+
| TelescopeGeneratedType
77+
| TsProtoGeneratedType
78+
| TsProto2GeneratedType
79+
| PbjsGeneratedType;
80+
81+
export function hasFromPartial(
82+
type: GeneratedType,
83+
): type is TelescopeGeneratedType | TsProtoGeneratedType | TsProto2GeneratedType {
84+
return (
85+
typeof (type as TelescopeGeneratedType | TsProtoGeneratedType | TsProto2GeneratedType).fromPartial ===
86+
"function"
87+
);
6088
}
6189

62-
export function isPbjsGeneratedType(type: GeneratedType): type is PbjsGeneratedType {
63-
return !isTsProtoGeneratedType(type);
90+
export function hasCreate(type: GeneratedType): type is PbjsGeneratedType {
91+
return typeof (type as PbjsGeneratedType).create === "function";
6492
}
6593

6694
const defaultTypeUrls = {
@@ -137,7 +165,7 @@ export class Registry {
137165
*
138166
* const Coin = registry.lookupType("/cosmos.base.v1beta1.Coin");
139167
* assert(Coin); // Ensures not unset
140-
* assert(isTsProtoGeneratedType(Coin)); // Ensures this is the type we expect
168+
* assert(hasFromPartial(Coin)); // Ensures this is the type we expect
141169
*
142170
* // Coin is typed TsProtoGeneratedType now.
143171
* ```
@@ -167,10 +195,7 @@ export class Registry {
167195
return this.encodeTxBody(value);
168196
}
169197
const type = this.lookupTypeWithError(typeUrl);
170-
const instance =
171-
isTelescopeGeneratedType(type) || isTsProtoGeneratedType(type)
172-
? type.fromPartial(value)
173-
: type.create(value);
198+
const instance = hasFromPartial(type) ? type.fromPartial(value) : type.create(value);
174199
return fixUint8Array(type.encode(instance).finish());
175200
}
176201

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// from https://github.com/stephenh/ts-proto/blob/v1.181.2/integration/reserved-words/reserved-words.ts
2+
3+
// Code generated by protoc-gen-ts_proto. DO NOT EDIT.
4+
// source: reserved-words.proto
5+
6+
/* eslint-disable */
7+
import * as _m0 from "protobufjs/minimal";
8+
9+
export const protobufPackage = "";
10+
11+
export interface Record {}
12+
13+
function createBaseRecord(): Record {
14+
return {};
15+
}
16+
17+
export const Record = {
18+
encode(_: Record, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer {
19+
return writer;
20+
},
21+
22+
decode(input: _m0.Reader | Uint8Array, length?: number): Record {
23+
const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input);
24+
let end = length === undefined ? reader.len : reader.pos + length;
25+
const message = createBaseRecord();
26+
while (reader.pos < end) {
27+
const tag = reader.uint32();
28+
switch (tag >>> 3) {
29+
}
30+
if ((tag & 7) === 4 || tag === 0) {
31+
break;
32+
}
33+
reader.skipType(tag & 7);
34+
}
35+
return message;
36+
},
37+
38+
fromJSON(_: any): Record {
39+
return {};
40+
},
41+
42+
toJSON(_: Record): unknown {
43+
const obj: any = {};
44+
return obj;
45+
},
46+
47+
create<I extends Exact<DeepPartial<Record>, I>>(base?: I): Record {
48+
return Record.fromPartial(base ?? ({} as any));
49+
},
50+
fromPartial<I extends Exact<DeepPartial<Record>, I>>(_: I): Record {
51+
const message = createBaseRecord();
52+
return message;
53+
},
54+
};
55+
56+
type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined;
57+
58+
export type DeepPartial<T> = T extends Builtin
59+
? T
60+
: T extends globalThis.Array<infer U>
61+
? globalThis.Array<DeepPartial<U>>
62+
: T extends ReadonlyArray<infer U>
63+
? ReadonlyArray<DeepPartial<U>>
64+
: T extends {}
65+
? { [K in keyof T]?: DeepPartial<T[K]> }
66+
: Partial<T>;
67+
68+
type KeysOfUnion<T> = T extends T ? keyof T : never;
69+
export type Exact<P, I extends P> = P extends Builtin
70+
? P
71+
: P & { [K in keyof P]: Exact<P[K], I[K]> } & { [K in Exclude<keyof I, KeysOfUnion<P>>]: never };
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// from https://github.com/stephenh/ts-proto/blob/v2.10.0/integration/reserved-words/reserved-words.ts
2+
3+
// Code generated by protoc-gen-ts_proto. DO NOT EDIT.
4+
// source: reserved-words.proto
5+
6+
/* eslint-disable */
7+
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
8+
9+
export const protobufPackage = "";
10+
11+
export interface Record {}
12+
13+
function createBaseRecord(): Record {
14+
return {};
15+
}
16+
17+
export const Record: MessageFns<Record> = {
18+
encode(_: Record, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
19+
return writer;
20+
},
21+
22+
decode(input: BinaryReader | Uint8Array, length?: number): Record {
23+
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
24+
const end = length === undefined ? reader.len : reader.pos + length;
25+
const message = createBaseRecord();
26+
while (reader.pos < end) {
27+
const tag = reader.uint32();
28+
switch (tag >>> 3) {
29+
}
30+
if ((tag & 7) === 4 || tag === 0) {
31+
break;
32+
}
33+
reader.skip(tag & 7);
34+
}
35+
return message;
36+
},
37+
38+
fromJSON(_: any): Record {
39+
return {};
40+
},
41+
42+
toJSON(_: Record): unknown {
43+
const obj: any = {};
44+
return obj;
45+
},
46+
47+
create<I extends Exact<DeepPartial<Record>, I>>(base?: I): Record {
48+
return Record.fromPartial(base ?? ({} as any));
49+
},
50+
fromPartial<I extends Exact<DeepPartial<Record>, I>>(_: I): Record {
51+
const message = createBaseRecord();
52+
return message;
53+
},
54+
};
55+
56+
type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined;
57+
58+
export type DeepPartial<T> = T extends Builtin
59+
? T
60+
: T extends globalThis.Array<infer U>
61+
? globalThis.Array<DeepPartial<U>>
62+
: T extends ReadonlyArray<infer U>
63+
? ReadonlyArray<DeepPartial<U>>
64+
: T extends {}
65+
? { [K in keyof T]?: DeepPartial<T[K]> }
66+
: Partial<T>;
67+
68+
type KeysOfUnion<T> = T extends T ? keyof T : never;
69+
export type Exact<P, I extends P> = P extends Builtin
70+
? P
71+
: P & { [K in keyof P]: Exact<P[K], I[K]> } & { [K in Exclude<keyof I, KeysOfUnion<P>>]: never };
72+
73+
export interface MessageFns<T> {
74+
encode(message: T, writer?: BinaryWriter): BinaryWriter;
75+
decode(input: BinaryReader | Uint8Array, length?: number): T;
76+
fromJSON(object: any): T;
77+
toJSON(message: T): unknown;
78+
create<I extends Exact<DeepPartial<T>, I>>(base?: I): T;
79+
fromPartial<I extends Exact<DeepPartial<T>, I>>(object: I): T;
80+
}

0 commit comments

Comments
 (0)