@cosmology/ast
Version:
Cosmos TypeScript AST generation
1,021 lines (935 loc) • 35.3 kB
text/typescript
import * as t from "@babel/types";
import { ProtoType } from "@cosmology/types";
import { BILLION, identifier, TypeLong } from "../../../utils";
import { ProtoParseContext } from "../../context";
import {
getDefaultTSTypeFromProtoType,
getFieldNames,
getDefaultTSTypeFromAminoTypeDefault,
} from "../../types";
import { getInterfaceToAminoName } from "../implements";
import { ToAminoJSONMethod } from "./index";
import { shouldOmitEmpty } from "@cosmology/utils";
const setValue = (
args: ToAminoJSONMethod,
valExpr?: t.Expression,
checkValExpr?: t.Expression
) => {
const { propName, origName } = getFieldNames(args.field);
const omitEmpty = shouldOmitEmpty(args.context, args.field);
valExpr =
valExpr ??
t.memberExpression(t.identifier("message"), t.identifier(propName));
checkValExpr = checkValExpr ?? valExpr;
if (omitEmpty) {
valExpr = t.conditionalExpression(
t.binaryExpression(
"===",
checkValExpr,
getDefaultTSTypeFromProtoType(
args.context,
args.field,
args.isOneOf,
true
)
),
t.identifier("undefined"),
valExpr
);
} else {
valExpr = t.logicalExpression(
"??",
valExpr,
getDefaultTSTypeFromProtoType(
args.context,
args.field,
args.isOneOf,
true
)
);
}
return t.expressionStatement(
t.assignmentExpression(
"=",
t.memberExpression(t.identifier("obj"), t.identifier(origName)),
valExpr
)
);
};
export const toAminoJSON = {
scalar(args: ToAminoJSONMethod, valExpr?: t.Expression) {
return setValue(args, valExpr);
},
string(args: ToAminoJSONMethod) {
let valueExpr: t.Expression;
let checkValExpr: t.Expression;
const useCosmosSDKDec = args.context.pluginValue(
"aminoEncoding.customTypes.useCosmosSDKDec"
);
const goPackage = args.context.ref.proto.options?.["go_package"];
if (useCosmosSDKDec) {
const isCosmosSDKDec =
(goPackage == "github.com/cosmos/cosmos-sdk/types" &&
args.field.options?.["(gogoproto.customtype)"] == "Dec") ||
args.field.options?.["(gogoproto.customtype)"] ==
"github.com/cosmos/cosmos-sdk/types.Dec" ||
args.field.options?.["(gogoproto.customtype)"] ==
"cosmossdk.io/math.LegacyDec";
if (isCosmosSDKDec) {
args.context.addUtil("Decimal");
const { propName } = getFieldNames(args.field);
valueExpr = t.memberExpression(
t.callExpression(
t.memberExpression(
t.identifier("Decimal"),
t.identifier("fromUserInput")
),
[
t.memberExpression(
t.identifier("message"),
t.identifier(propName)
),
t.numericLiteral(18),
]
),
t.identifier("atomics")
);
checkValExpr = t.memberExpression(
t.identifier("message"),
t.identifier(propName)
);
}
}
return setValue(args, valueExpr, checkValExpr);
},
double(args: ToAminoJSONMethod) {
return toAminoJSON.scalar(args);
},
float(args: ToAminoJSONMethod) {
return toAminoJSON.scalar(args);
},
bool(args: ToAminoJSONMethod) {
return toAminoJSON.scalar(args);
},
number(args: ToAminoJSONMethod) {
return toAminoJSON.scalar(args);
},
int32(args: ToAminoJSONMethod) {
return toAminoJSON.scalar(args);
},
uint32(args: ToAminoJSONMethod) {
return toAminoJSON.scalar(args);
},
sint32(args: ToAminoJSONMethod) {
return toAminoJSON.scalar(args);
},
fixed32(args: ToAminoJSONMethod) {
return toAminoJSON.scalar(args);
},
sfixed32(args: ToAminoJSONMethod) {
return toAminoJSON.scalar(args);
},
// obj.big = message.big ? message.big.toString() : "0";
// obj.o_big = message.oBig !== BigInt(0) ? message.oBig.toString() : undefined;
long(args: ToAminoJSONMethod) {
const { propName, origName } = getFieldNames(args.field);
const omitEmpty = shouldOmitEmpty(args.context, args.field);
const nullTest = omitEmpty
? TypeLong.getLongNotZero(propName, args.context)
: t.memberExpression(
t.identifier("message"),
t.identifier(propName)
);
const template = require("@babel/template").default;
// Create a template that represents message.oBig.toString()
const expressionTemplate = template.expression(`
MESSAGE.OBJECT?.METHOD()
`);
// Generate AST node from the template by providing the identifiers
const messageNode = expressionTemplate({
MESSAGE: t.identifier("message"),
OBJECT: t.identifier(propName),
METHOD: t.identifier("toString"),
});
return t.expressionStatement(
t.assignmentExpression(
"=",
t.memberExpression(t.identifier("obj"), t.identifier(origName)),
t.conditionalExpression(
nullTest,
messageNode,
omitEmpty ? t.identifier("undefined") : t.stringLiteral("0")
)
)
);
},
int64(args: ToAminoJSONMethod) {
return toAminoJSON.long(args);
},
uint64(args: ToAminoJSONMethod) {
return toAminoJSON.long(args);
},
sint64(args: ToAminoJSONMethod) {
return toAminoJSON.long(args);
},
fixed64(args: ToAminoJSONMethod) {
return toAminoJSON.long(args);
},
sfixed64(args: ToAminoJSONMethod) {
return toAminoJSON.long(args);
},
// obj.proto = message.proto ? AccessConfig.toAmino(message.proto) : AccessConfig.toAmino(AccessConfig.fromPartial({}));
// obj.o_proto = message.oProto ? AccessConfig.toAmino(message.oProto) : undefined;
protoType(args: ToAminoJSONMethod) {
const { propName, origName } = getFieldNames(args.field);
const name = args.context.getTypeName(args.field);
const omitEmpty = shouldOmitEmpty(args.context, args.field);
let defaultValue: t.Expression = omitEmpty
? t.identifier("undefined")
: getDefaultTSTypeFromAminoTypeDefault(args.context, args.field);
if (args.field.type === "ibc.core.client.v1.Height") {
defaultValue = t.objectExpression([]);
}
return t.expressionStatement(
t.assignmentExpression(
"=",
t.memberExpression(t.identifier("obj"), t.identifier(origName)),
t.conditionalExpression(
t.memberExpression(
t.identifier("message"),
t.identifier(propName)
),
t.callExpression(
t.memberExpression(
t.identifier(name),
t.identifier("toAmino")
),
[
t.memberExpression(
t.identifier("message"),
t.identifier(propName)
),
...(args.context.options.interfaces.enabled &&
args.context.options.interfaces
.useUseInterfacesParams
? [t.identifier("useInterfaces")]
: []),
]
),
defaultValue
)
)
);
},
anyType(args: ToAminoJSONMethod) {
const { propName, origName } = getFieldNames(args.field);
const accepts_interface =
args.field.options["(cosmos_proto.accepts_interface)"];
const interfaceName = accepts_interface.split(",")[0];
const interfaceFnName = getInterfaceToAminoName(interfaceName);
args.context.getTypeName(args.field);
const omitEmpty = shouldOmitEmpty(args.context, args.field);
let defaultValue: t.Expression = !omitEmpty
? t.objectExpression([
t.objectProperty(t.identifier("type"), t.stringLiteral("")),
t.objectProperty(
t.identifier("value"),
t.objectExpression([])
),
])
: t.identifier("undefined");
let aminoFuncExpr: t.Expression = t.callExpression(
t.identifier(interfaceFnName),
[
t.tsAsExpression(
t.memberExpression(
t.identifier("message"),
t.identifier(propName)
),
t.tsTypeReference(t.identifier("Any"))
),
...(args.context.options.interfaces.enabled &&
args.context.options.interfaces.useUseInterfacesParams
? [t.identifier("useInterfaces")]
: []),
]
);
const isGlobalRegistry =
args.context.options.interfaces?.useGlobalDecoderRegistry;
if (isGlobalRegistry) {
aminoFuncExpr = t.callExpression(
t.memberExpression(
t.identifier("GlobalDecoderRegistry"),
t.identifier("toAminoMsg")
),
[
t.memberExpression(
t.identifier("message"),
t.identifier(propName)
),
]
);
}
return t.expressionStatement(
t.assignmentExpression(
"=",
t.memberExpression(t.identifier("obj"), t.identifier(origName)),
t.conditionalExpression(
t.memberExpression(
t.identifier("message"),
t.identifier(propName)
),
aminoFuncExpr,
defaultValue
)
)
);
},
type(args: ToAminoJSONMethod) {
if (
!args.context.options.aminoEncoding.useLegacyInlineEncoding &&
args.context.options.interfaces.enabled &&
args.field.type === "google.protobuf.Any" &&
args.field.options["(cosmos_proto.accepts_interface)"]
) {
return toAminoJSON.anyType(args);
}
return toAminoJSON.protoType(args);
},
enum(args: ToAminoJSONMethod) {
return toAminoJSON.scalar(args);
},
bytes(args: ToAminoJSONMethod) {
args.context.addUtil("base64FromBytes");
const { propName, origName } = getFieldNames(args.field);
const omitEmpty = shouldOmitEmpty(args.context, args.field);
let defaultValue: t.Expression = !omitEmpty
? t.stringLiteral("")
: t.identifier("undefined");
const expr = t.callExpression(t.identifier("base64FromBytes"), [
t.memberExpression(t.identifier("message"), t.identifier(propName)),
]);
return t.expressionStatement(
t.assignmentExpression(
"=",
t.memberExpression(t.identifier("obj"), t.identifier(origName)),
t.conditionalExpression(
t.memberExpression(
t.identifier("message"),
t.identifier(propName)
),
expr,
defaultValue
)
)
);
},
duration(args: ToAminoJSONMethod) {
return toAminoJSON.type(args);
},
timestamp(args: ToAminoJSONMethod) {
const timestampFormat = args.context.pluginValue(
"prototypes.typingsFormat.timestamp"
);
switch (timestampFormat) {
case "timestamp":
return toAminoJSON.type(args);
case "date":
default:
return toAminoJSON.timestampDate(args);
}
},
timestampDate(args: ToAminoJSONMethod) {
const { propName, origName } = getFieldNames(args.field);
args.context.addUtil("toTimestamp");
const omitEmpty = shouldOmitEmpty(args.context, args.field);
let defaultValue: t.Expression = !omitEmpty
? getDefaultTSTypeFromProtoType(
args.context,
args.field,
args.isOneOf,
true
)
: t.identifier("undefined");
return t.expressionStatement(
t.assignmentExpression(
"=",
t.memberExpression(t.identifier("obj"), t.identifier(origName)),
t.conditionalExpression(
t.memberExpression(
t.identifier("message"),
t.identifier(propName)
),
t.callExpression(
t.memberExpression(
t.identifier("Timestamp"),
t.identifier("toAmino")
),
[
t.callExpression(t.identifier("toTimestamp"), [
t.memberExpression(
t.identifier("message"),
t.identifier(propName)
),
]),
]
),
defaultValue
)
)
);
},
pubkey(args: ToAminoJSONMethod) {
args.context.addUtil("decodePubkey");
const { propName, origName } = getFieldNames(args.field);
const omitEmpty = shouldOmitEmpty(args.context, args.field);
let defaultValue: t.Expression = !omitEmpty
? getDefaultTSTypeFromProtoType(
args.context,
args.field,
args.isOneOf,
true
)
: t.identifier("undefined");
return t.expressionStatement(
t.assignmentExpression(
"=",
t.memberExpression(t.identifier("obj"), t.identifier(origName)),
t.conditionalExpression(
t.memberExpression(
t.identifier("message"),
t.identifier(propName)
),
//
t.callExpression(t.identifier("decodePubkey"), [
t.memberExpression(
t.identifier("message"),
t.identifier(propName)
),
]),
//
defaultValue
)
)
);
},
rawBytes(args: ToAminoJSONMethod) {
args.context.addUtil("fromUtf8");
const { propName, origName } = getFieldNames(args.field);
const omitEmpty = shouldOmitEmpty(args.context, args.field);
let defaultValue: t.Expression = !omitEmpty
? t.objectExpression([])
: t.identifier("undefined");
return t.expressionStatement(
t.assignmentExpression(
"=",
t.memberExpression(t.identifier("obj"), t.identifier(origName)),
t.conditionalExpression(
t.memberExpression(
t.identifier("message"),
t.identifier(propName)
),
//
t.callExpression(
t.memberExpression(
t.identifier("JSON"),
t.identifier("parse")
),
[
t.callExpression(t.identifier("fromUtf8"), [
t.memberExpression(
t.identifier("message"),
t.identifier(propName)
),
]),
]
),
//
defaultValue
)
)
);
},
wasmByteCode(args: ToAminoJSONMethod) {
args.context.addUtil("toBase64");
const { propName, origName } = getFieldNames(args.field);
const omitEmpty = shouldOmitEmpty(args.context, args.field);
let defaultValue: t.Expression = !omitEmpty
? t.stringLiteral("")
: t.identifier("undefined");
return t.expressionStatement(
t.assignmentExpression(
"=",
t.memberExpression(t.identifier("obj"), t.identifier(origName)),
t.conditionalExpression(
t.memberExpression(
t.identifier("message"),
t.identifier(propName)
),
//
t.callExpression(t.identifier("toBase64"), [
t.memberExpression(
t.identifier("message"),
t.identifier(propName)
),
]),
//
defaultValue
)
)
);
},
keyHash(args: ToAminoJSONMethod) {
const { propName, origName } = getFieldNames(args.field);
const keyType = args.field.keyType;
const valueType = args.field.parsedType.name;
let toAminoJSON = null;
switch (valueType) {
case "string":
toAminoJSON = t.identifier("v");
break;
case "uint32":
case "int32":
toAminoJSON = t.callExpression(
t.memberExpression(
t.identifier("Math"),
t.identifier("round")
),
[t.identifier("v")]
);
break;
case "int64":
case "uint64":
toAminoJSON = t.callExpression(
t.memberExpression(
t.identifier("v"),
t.identifier("toString")
),
[]
);
break;
default:
toAminoJSON = t.callExpression(
t.memberExpression(
t.identifier(valueType),
t.identifier("toAmino")
),
[t.identifier("v")]
);
}
return [
t.expressionStatement(
t.assignmentExpression(
"=",
t.memberExpression(
t.identifier("obj"),
t.identifier(origName)
),
t.objectExpression([])
)
),
//
t.ifStatement(
t.memberExpression(
t.identifier("message"),
t.identifier(propName)
),
t.blockStatement([
t.expressionStatement(
t.callExpression(
t.memberExpression(
t.callExpression(
t.memberExpression(
t.identifier("Object"),
t.identifier("entries")
),
[
t.memberExpression(
t.identifier("message"),
t.identifier(propName)
),
]
),
t.identifier("forEach")
),
[
t.arrowFunctionExpression(
[
t.arrayPattern([
t.identifier("k"),
t.identifier("v"),
]),
],
t.blockStatement([
t.expressionStatement(
t.assignmentExpression(
"=",
t.memberExpression(
t.memberExpression(
t.identifier("obj"),
t.identifier(origName)
),
t.identifier("k"),
true
),
toAminoJSON
)
),
])
),
]
)
),
])
),
];
},
array(args: ToAminoJSONMethod, expr: t.Expression) {
const { propName, origName } = getFieldNames(args.field);
return t.ifStatement(
t.memberExpression(t.identifier("message"), t.identifier(propName)),
t.blockStatement([
t.expressionStatement(
t.assignmentExpression(
"=",
t.memberExpression(
t.identifier("obj"),
t.identifier(origName)
),
t.callExpression(
t.memberExpression(
t.memberExpression(
t.identifier("message"),
t.identifier(propName)
),
t.identifier("map")
),
[
t.arrowFunctionExpression(
[t.identifier("e")],
expr
),
]
)
)
),
]),
t.blockStatement([
t.expressionStatement(
t.assignmentExpression(
"=",
t.memberExpression(
t.identifier("obj"),
t.identifier(origName)
),
t.memberExpression(
t.identifier("message"),
t.identifier(propName)
)
)
),
])
);
},
};
export const arrayTypes = {
scalar() {
return t.identifier("e");
},
string(args: ToAminoJSONMethod) {
const useCosmosSDKDec = args.context.pluginValue(
"aminoEncoding.customTypes.useCosmosSDKDec"
);
if (useCosmosSDKDec) {
const isCosmosSDKDec =
args.field.options?.["(gogoproto.customtype)"] ==
"github.com/cosmos/cosmos-sdk/types.Dec" ||
args.field.options?.["(gogoproto.customtype)"] ==
"cosmossdk.io/math.LegacyDec";
if (isCosmosSDKDec) {
args.context.addUtil("Decimal");
return t.memberExpression(
t.callExpression(
t.memberExpression(
t.identifier("Decimal"),
t.identifier("fromUserInput")
),
[
t.identifier("e"),
t.numericLiteral(18),
]
),
t.identifier("atomics")
);
}
}
return arrayTypes.scalar();
},
double() {
return arrayTypes.scalar();
},
float() {
return arrayTypes.scalar();
},
bool() {
return arrayTypes.scalar();
},
number() {
return arrayTypes.scalar();
},
int32() {
return arrayTypes.number();
},
uint32() {
return arrayTypes.number();
},
sint32() {
return arrayTypes.number();
},
fixed32() {
return arrayTypes.number();
},
sfixed32() {
return arrayTypes.number();
},
long(args: ToAminoJSONMethod) {
return TypeLong.getToStringArray(args.context);
},
int64(args: ToAminoJSONMethod) {
return arrayTypes.long(args);
},
uint64(args: ToAminoJSONMethod) {
return arrayTypes.long(args);
},
sint64(args: ToAminoJSONMethod) {
return arrayTypes.long(args);
},
fixed64(args: ToAminoJSONMethod) {
return arrayTypes.long(args);
},
sfixed64(args: ToAminoJSONMethod) {
return arrayTypes.long(args);
},
rawBytes(args: ToAminoJSONMethod) {
args.context.addUtil("fromUtf8");
return t.callExpression(
t.memberExpression(t.identifier("JSON"), t.identifier("parse")),
[t.callExpression(t.identifier("fromUtf8"), [t.identifier("e")])]
);
},
wasmByteCode(args: ToAminoJSONMethod) {
args.context.addUtil("toBase64");
return t.callExpression(t.identifier("toBase64"), [t.identifier("e")]);
},
bytes(args: ToAminoJSONMethod) {
// bytes [RawContractMessage]
if (
args.field.options?.["(gogoproto.casttype)"] ===
"RawContractMessage"
) {
return arrayTypes.rawBytes(args);
}
// bytes [WASMByteCode]
if (args.field.options?.["(gogoproto.customname)"] === "WASMByteCode") {
return arrayTypes.wasmByteCode(args);
}
//default
args.context.addUtil("base64FromBytes");
return t.callExpression(t.identifier("base64FromBytes"), [
t.identifier("e"),
]);
},
enum() {
return arrayTypes.scalar();
},
anyType(args: ToAminoJSONMethod) {
const { propName, origName } = getFieldNames(args.field);
const accepts_interface =
args.field.options["(cosmos_proto.accepts_interface)"];
const interfaceName = accepts_interface.split(",")[0];
const interfaceFnName = getInterfaceToAminoName(interfaceName);
const isGlobalRegistry =
args.context.options.interfaces?.useGlobalDecoderRegistry;
let aminoFuncExpr: t.Expression = t.callExpression(
t.identifier(interfaceFnName),
[
t.tsAsExpression(
t.identifier("e"),
t.tsTypeReference(t.identifier("Any"))
),
...(args.context.options.interfaces.enabled &&
args.context.options.interfaces.useUseInterfacesParams
? [t.identifier("useInterfaces")]
: []),
]
);
if (isGlobalRegistry) {
aminoFuncExpr = t.callExpression(
t.memberExpression(
t.identifier("GlobalDecoderRegistry"),
t.identifier("toAminoMsg")
),
[t.identifier("e")]
);
}
return t.conditionalExpression(
t.identifier("e"),
aminoFuncExpr,
t.identifier("undefined")
);
},
protoType(args: ToAminoJSONMethod) {
const name = args.context.getTypeName(args.field);
return t.conditionalExpression(
t.identifier("e"),
t.callExpression(
t.memberExpression(t.identifier(name), t.identifier("toAmino")),
[
t.identifier("e"),
...(args.context.options.interfaces.enabled &&
args.context.options.interfaces.useUseInterfacesParams
? [t.identifier("useInterfaces")]
: []),
]
),
t.identifier("undefined")
);
},
type(args: ToAminoJSONMethod) {
if (
!args.context.options.aminoEncoding.useLegacyInlineEncoding &&
args.context.options.interfaces.enabled &&
args.field.type === "google.protobuf.Any" &&
args.field.options["(cosmos_proto.accepts_interface)"]
) {
return arrayTypes.anyType(args);
}
return arrayTypes.protoType(args);
},
};
export const toAminoMessages = {
anyType() {
return [
t.variableDeclaration("const", [
t.variableDeclarator(
identifier("obj", t.tsTypeAnnotation(t.tsAnyKeyword())),
t.objectExpression([])
),
]),
t.expressionStatement(
t.assignmentExpression(
"=",
t.memberExpression(
t.identifier("obj"),
t.identifier("type")
),
t.memberExpression(
t.identifier("message"),
t.identifier("typeUrl")
)
)
),
t.expressionStatement(
t.assignmentExpression(
"=",
t.memberExpression(
t.identifier("obj"),
t.identifier("value")
),
t.memberExpression(
t.identifier("message"),
t.identifier("value")
)
)
),
t.returnStatement(t.identifier("obj")),
];
},
timestamp(context: ProtoParseContext, name: string, proto: ProtoType) {
context.addUtil("fromTimestamp");
return t.returnStatement(
t.callExpression(
t.memberExpression(
t.callExpression(
t.memberExpression(
t.callExpression(t.identifier("fromTimestamp"), [
t.identifier("message"),
]),
t.identifier("toISOString")
),
[]
),
t.identifier("replace")
),
[t.regExpLiteral("\\.\\d+Z$"), t.stringLiteral("Z")]
)
);
},
duration(context: ProtoParseContext, name: string, proto: ProtoType) {
const longType = TypeLong.getType(context);
switch (longType) {
case "BigInt":
return t.returnStatement(
t.callExpression(
t.memberExpression(
t.parenthesizedExpression(
t.binaryExpression(
"+",
t.binaryExpression(
"*",
t.memberExpression(
t.identifier("message"),
t.identifier("seconds")
),
t.callExpression(
t.identifier("BigInt"),
[t.stringLiteral("1000000000")]
)
),
t.callExpression(t.identifier("BigInt"), [
t.memberExpression(
t.identifier("message"),
t.identifier("nanos")
),
])
)
),
t.identifier("toString")
),
[]
)
);
case "Long":
default:
return t.returnStatement(
t.callExpression(
t.memberExpression(
t.binaryExpression(
"+",
t.binaryExpression(
"*",
t.callExpression(
t.memberExpression(
t.memberExpression(
t.identifier("message"),
t.identifier("seconds")
),
t.identifier("toInt")
),
[]
),
BILLION
),
t.memberExpression(
t.identifier("message"),
t.identifier("nanos")
)
),
t.identifier("toString")
),
[]
)
);
}
},
};