UNPKG

@cosmology/ast

Version:
1,006 lines (911 loc) 35.2 kB
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) => { const { propName, origName } = getFieldNames(args.field); const omitEmpty = shouldOmitEmpty(args.context, args.field); valExpr = valExpr ?? t.memberExpression(t.identifier("message"), t.identifier(propName)); if (omitEmpty) { valExpr = t.conditionalExpression(t.binaryExpression( "===", valExpr, 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; 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('padDecimal'); const { propName } = getFieldNames(args.field); valueExpr = t.callExpression( t.identifier('padDecimal'), [ t.memberExpression( t.identifier('message'), t.identifier(propName) ) ] ) } } return setValue(args, valueExpr); }, 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 interfaceName = args.field.options['(cosmos_proto.accepts_interface)']; 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('padDecimal'); const { propName } = getFieldNames(args.field); return t.callExpression( t.identifier('padDecimal'), [ t.identifier('e') ] ) } } 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 interfaceName = args.field.options['(cosmos_proto.accepts_interface)']; 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') ), [] ) ); } } }