From 211e61a015ada5034ffac69025409214c06ac767 Mon Sep 17 00:00:00 2001 From: dafuga Date: Fri, 18 Aug 2023 20:17:40 -0500 Subject: [PATCH] chore: got codegen script geenrating correct rewards.gm helper class --- src/codegen.ts | 31 +++++++----- src/codegen/contract.ts | 63 ++++++++++++++++++++++-- src/codegen/helpers.ts | 24 ++++------ src/codegen/interfaces.ts | 26 ++++++---- src/codegen/maps.ts | 30 ++++++++++++ src/codegen/structs.ts | 3 +- test/data/contracts/mock-rewards.ts | 5 -- test/tmp/rewards.gm.ts | 74 ++++++++++++++++++----------- 8 files changed, 180 insertions(+), 76 deletions(-) create mode 100644 src/codegen/maps.ts diff --git a/src/codegen.ts b/src/codegen.ts index 7ee8da4..a7becaa 100644 --- a/src/codegen.ts +++ b/src/codegen.ts @@ -2,17 +2,15 @@ import * as ts from 'typescript' import * as prettier from 'prettier' import { - EOSIO_CORE_CLASSES, - EOSIO_CORE_TYPES, generateImportStatement, - generateInterface, getCoreImports, } from './codegen/helpers' import {generateNamespace, generateNamespaceName} from './codegen/namespace' import {generateContractClass} from './codegen/contract' import {abiToBlob} from './utils' -import { generateStructClasses } from './codegen/structs' -import { generateActionNamesInterface, generateActionsNamespace } from './codegen/interfaces' +import {generateStructClasses} from './codegen/structs' +import {generateActionNamesInterface, generateActionsNamespace} from './codegen/interfaces' +import {generateTableMap} from './codegen/maps' const printer = ts.createPrinter() @@ -38,14 +36,14 @@ export async function codegen(contractName, abi) { const importCoreStatement = generateImportStatement( sessionImports, '@wharfkit/session' - ) + ) + + const {classDeclaration} = await generateContractClass(contractName, abi) const actionNamesInterface = generateActionNamesInterface(abi) const actionsNamespace = generateActionsNamespace(abi) - const {classDeclaration} = await generateContractClass(contractName, abi) - // Iterate through structs and create struct classes with fields const structDeclarations = generateStructClasses(abi) @@ -98,18 +96,28 @@ export async function codegen(contractName, abi) { ) ) + const tableMap = generateTableMap(abi) + + const exportStatement = ts.factory.createExportAssignment( + undefined, + undefined, + false, + ts.factory.createIdentifier(namespaceName) + ); + // Generate types namespace const namespaceDeclaration = generateNamespace(namespaceName, [ abiBlobField, abiField, - // actionNamesInterface, - actionsNamespace, classDeclaration, + actionNamesInterface, + actionsNamespace, generateNamespace('Types', structDeclarations), + tableMap, ]) const sourceFile = ts.factory.createSourceFile( - [importContractStatement, importCoreStatement, namespaceDeclaration], + [importContractStatement, importCoreStatement, namespaceDeclaration, exportStatement], ts.factory.createToken(ts.SyntaxKind.EndOfFileToken), ts.NodeFlags.None ) @@ -119,5 +127,6 @@ export async function codegen(contractName, abi) { } catch (e) { console.error(`An error occurred while generating the contract code: ${e}`) + throw e } } diff --git a/src/codegen/contract.ts b/src/codegen/contract.ts index f1aee98..908bfb3 100644 --- a/src/codegen/contract.ts +++ b/src/codegen/contract.ts @@ -45,10 +45,14 @@ export async function generateContractClass(contractName: string, abi: ABI.Def) classMembers.push(constructorMember) - const actionMethod = generateActionFunction(abi) + const actionMethod = generateActionMethod(abi) classMembers.push(actionMethod) + const tableMethod = generateTableMethod(abi) + + classMembers.push(tableMethod) + // Construct class declaration const classDeclaration = generateClassDeclaration('Contract', classMembers, { @@ -93,7 +97,7 @@ function generateConstructorFunction(contractName): ts.ExpressionStatement { ) } -function generateActionFunction(abi: ABI.Def): ts.MethodDeclaration { +function generateActionMethod(abi: ABI.Def): ts.MethodDeclaration { const typeParameter = ts.factory.createTypeParameterDeclaration( "T", ts.factory.createUnionTypeNode( @@ -120,7 +124,7 @@ function generateActionFunction(abi: ABI.Def): ts.MethodDeclaration { undefined, "data", undefined, - ts.factory.createTypeReferenceNode("ActionNameParams", [ts.factory.createTypeReferenceNode("T")]), + ts.factory.createTypeReferenceNode("ActionNameParams[T]"), undefined ); @@ -135,7 +139,7 @@ function generateActionFunction(abi: ABI.Def): ts.MethodDeclaration { ); // 4. Generate the function body. - const functionBody = ts.factory.createBlock([ + const methodBody = ts.factory.createBlock([ ts.factory.createReturnStatement( ts.factory.createCallExpression( ts.factory.createPropertyAccessExpression( @@ -157,6 +161,55 @@ function generateActionFunction(abi: ABI.Def): ts.MethodDeclaration { [typeParameter], [nameParameter, dataParameter, optionsParameter], ts.factory.createTypeReferenceNode("Action"), - functionBody + methodBody + ); +} + +function generateTableMethod(abi: ABI.Def): ts.MethodDeclaration { + const typeParameter = ts.factory.createTypeParameterDeclaration( + "T", + ts.factory.createUnionTypeNode( + abi.tables.map(table => + ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral(String(table.name))) + ) + ) + ); + + // 3. Create the function parameters. + const nameParameter = ts.factory.createParameterDeclaration( + undefined, + undefined, + undefined, + "name", + undefined, + ts.factory.createTypeReferenceNode("T"), + undefined + ); + + // 4. Generate the function body. + const methodBody = ts.factory.createBlock([ + ts.factory.createReturnStatement( + ts.factory.createCallExpression( + ts.factory.createPropertyAccessExpression( + ts.factory.createSuper(), + ts.factory.createIdentifier("table") + ), + undefined, + [ts.factory.createIdentifier("name"), ts.factory.createIdentifier("TableMap[name]")] + ) + ) + ], true); + + return ts.factory.createMethodDeclaration( + undefined, + undefined, + undefined, + 'table', + undefined, + [typeParameter], + [nameParameter], + undefined, + methodBody ); } + diff --git a/src/codegen/helpers.ts b/src/codegen/helpers.ts index 4ecb8f8..83d8cb7 100644 --- a/src/codegen/helpers.ts +++ b/src/codegen/helpers.ts @@ -142,7 +142,7 @@ export function findCoreType(type: string): string | undefined { } } -export function findInternalType(type: string, namespace: string | null, abi: ABI.Def): string { +export function findInternalType(type: string, typeNamespace: string | null, abi: ABI.Def): string { let { type: typeString } = extractDecorator(type) const relevantAbitype = findAbiType(typeString, abi) @@ -151,13 +151,13 @@ export function findInternalType(type: string, namespace: string | null, abi: AB typeString = relevantAbitype } - const variantType = findVariantType(typeString, namespace, abi) + const variantType = findVariantType(typeString, typeNamespace, abi) if (variantType) { typeString = variantType } - return formatInternalType(typeString, namespace, abi) + return formatInternalType(typeString, typeNamespace, abi) } function formatInternalType(typeString: string, namespace: string | null, abi: ABI.Def): string { @@ -188,28 +188,20 @@ function findVariantType(typeString: string, namespace: string | null, abi: ABI. .join(' | ') } -function findAbiType(typeString: string, abi: ABI.Def): string | undefined { - // console.log('findAbiType', {typeString, abi}) +export function findAbiType(typeString: string, abi: ABI.Def, typeNamespace = ''): string | undefined { const abiType = abi.structs.find( - (abiType) => { - // console.log({abiType, typeString}) - return abiType.name.toLowerCase() === typeString.toLowerCase() - } + (abiType) => abiType.name === typeString )?.name - console.log({formattedAbiType: abiType && `Types.${generateStructClassName(abiType)}` }) - if (abiType) { - return `Types.${generateStructClassName(abiType)}` + return `${typeNamespace}${generateStructClassName(abiType)}` } } -export function findExternalType(type: string, abi: ABI.Def): string { +export function findExternalType(type: string, abi: ABI.Def, typeNamespace?: string): string { let { type: typeString, decorator } = extractDecorator(type) - const relevantAbitype = findAbiType(typeString, abi) - - console.log({typeString, relevantAbitype}) + const relevantAbitype = findAbiType(typeString, abi, typeNamespace) if (relevantAbitype) { typeString = relevantAbitype diff --git a/src/codegen/interfaces.ts b/src/codegen/interfaces.ts index cb944c3..2334608 100644 --- a/src/codegen/interfaces.ts +++ b/src/codegen/interfaces.ts @@ -1,21 +1,30 @@ import { ABI } from "@wharfkit/session"; import ts from "typescript"; -import { findCoreType, findExternalType } from "./helpers"; +import { capitalize } from "../utils"; +import { findExternalType } from "./helpers"; import { getActionFieldFromAbi } from "./structs"; -export function generateActionNamesInterface(abi: ABI.Def): ts.PropertySignature[] { +export function generateActionNamesInterface(abi: ABI.Def): ts.InterfaceDeclaration { // Generate property signatures for each action - return abi.actions.map(action => { + const members = abi.actions.map(action => { const actionName = String(action.name); - const actionNameCapitalized = actionName.charAt(0).toUpperCase() + actionName.slice(1); + const actionNameCapitalized = capitalize(actionName); return ts.factory.createPropertySignature( undefined, actionName, undefined, - ts.factory.createTypeReferenceNode(`ActionParams.${actionNameCapitalized}`, undefined) + ts.factory.createTypeReferenceNode(`ActionParams.${actionNameCapitalized}`) ); }); + + return ts.factory.createInterfaceDeclaration( + [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)], + "ActionNameParams", + undefined, + undefined, + members + ); } export function generateActionInterface(actionStruct, abi): ts.InterfaceDeclaration { @@ -23,14 +32,14 @@ export function generateActionInterface(actionStruct, abi): ts.InterfaceDeclarat return ts.factory.createPropertySignature( undefined, field.name.toLowerCase(), - field.optional ? ts.factory.createToken(ts.SyntaxKind.QuestionToken) : undefined, - ts.factory.createTypeReferenceNode(findExternalType(field.type, abi), undefined) + field.type.includes('?') ? ts.factory.createToken(ts.SyntaxKind.QuestionToken) : undefined, + ts.factory.createTypeReferenceNode(findExternalType(field.type, abi, 'Types.'), undefined) ); }); return ts.factory.createInterfaceDeclaration( [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)], - actionStruct.structName, + capitalize(actionStruct.structName), undefined, undefined, members @@ -40,7 +49,6 @@ export function generateActionInterface(actionStruct, abi): ts.InterfaceDeclarat export function generateActionsNamespace(abi: ABI.Def): ts.ModuleDeclaration { const actionStructsWithFields = getActionFieldFromAbi(abi); - const interfaces = abi.actions.map((action) => { const actionStruct = actionStructsWithFields.find( actionStructWithField => actionStructWithField.structName === action.type diff --git a/src/codegen/maps.ts b/src/codegen/maps.ts new file mode 100644 index 0000000..5b425b8 --- /dev/null +++ b/src/codegen/maps.ts @@ -0,0 +1,30 @@ +import { ABI } from '@wharfkit/session'; +import * as ts from 'typescript'; +import { findAbiType } from './helpers'; + +export function generateTableMap(abi: ABI.Def): ts.VariableStatement { + // Map over tables to create the object properties + const tableProperties = abi.tables.map(table => + ts.factory.createPropertyAssignment( + String(table.name), + ts.factory.createIdentifier(findAbiType(table.type, abi, 'Types.') || table.type) + ) + ); + + // Create the TableMap structure + const tableMap = ts.factory.createObjectLiteralExpression(tableProperties, true); + + // Declare the variable + return ts.factory.createVariableStatement( + undefined, + ts.factory.createVariableDeclarationList( + [ts.factory.createVariableDeclaration( + ts.factory.createIdentifier('TableMap'), + undefined, + undefined, + tableMap + )], + ts.NodeFlags.Const + ) + ); +} diff --git a/src/codegen/structs.ts b/src/codegen/structs.ts index c899c26..ccff39d 100644 --- a/src/codegen/structs.ts +++ b/src/codegen/structs.ts @@ -1,5 +1,6 @@ import { ABI } from "@wharfkit/session" import ts from "typescript" +import { capitalize } from "../utils" import { findInternalType, generateStructClassName, extractDecorator } from "./helpers" interface FieldType { @@ -34,7 +35,7 @@ export function getActionFieldFromAbi(abi: any): StructData[] { for (const field of struct.fields) { fields.push({ - name: field.name.charAt(0).toUpperCase() + field.name.slice(1), + name: capitalize(field.name), type: field.type, }) } diff --git a/test/data/contracts/mock-rewards.ts b/test/data/contracts/mock-rewards.ts index 74b3dee..f1c2d09 100644 --- a/test/data/contracts/mock-rewards.ts +++ b/test/data/contracts/mock-rewards.ts @@ -62,10 +62,6 @@ export namespace RewardsGm { export interface Deluser { account: NameType } - export interface OraclePair { - name: NameType - precision: UInt16Type - } export interface Receipt { account: NameType amount: AssetType @@ -161,5 +157,4 @@ export namespace RewardsGm { users: Types.UserRow, } } - export default RewardsGm diff --git a/test/tmp/rewards.gm.ts b/test/tmp/rewards.gm.ts index a427d20..6ba2b06 100644 --- a/test/tmp/rewards.gm.ts +++ b/test/tmp/rewards.gm.ts @@ -18,49 +18,60 @@ export namespace RewardsGm { 'DmVvc2lvOjphYmkvMS4yAAoHYWRkdXNlcgACB2FjY291bnQEbmFtZQZ3ZWlnaHQGdWludDE2BWNsYWltAAIHYWNjb3VudARuYW1lBmFtb3VudAZhc3NldD8GY29uZmlnAAMMdG9rZW5fc3ltYm9sBnN5bWJvbA5vcmFjbGVfYWNjb3VudARuYW1lDG9yYWNsZV9wYWlycw1vcmFjbGVfcGFpcltdCWNvbmZpZ3VyZQADDHRva2VuX3N5bWJvbAZzeW1ib2wOb3JhY2xlX2FjY291bnQEbmFtZQxvcmFjbGVfcGFpcnMNb3JhY2xlX3BhaXJbXQdkZWx1c2VyAAEHYWNjb3VudARuYW1lC29yYWNsZV9wYWlyAAIEbmFtZQRuYW1lCXByZWNpc2lvbgZ1aW50MTYKcHJpY2VfaW5mbwADBHBhaXIGc3RyaW5nBXByaWNlB2Zsb2F0NjQJdGltZXN0YW1wCnRpbWVfcG9pbnQHcmVjZWlwdAADB2FjY291bnQEbmFtZQZhbW91bnQFYXNzZXQGdGlja2VyDHByaWNlX2luZm9bXQp1cGRhdGV1c2VyAAIHYWNjb3VudARuYW1lBndlaWdodAZ1aW50MTYIdXNlcl9yb3cAAwdhY2NvdW50BG5hbWUGd2VpZ2h0BnVpbnQxNgdiYWxhbmNlBWFzc2V0BgAAAOAqrFMyB2FkZHVzZXKVAi0tLQpzcGVjX3ZlcnNpb246ICIwLjIuMCIKdGl0bGU6IEFkZCB1c2VyCnN1bW1hcnk6ICdBZGQgbmV3IHVzZXIge3tub3dyYXAgYWNjb3VudH19JwppY29uOiBodHRwczovL2FsbW9zdC5kaWdpdGFsL2ltYWdlcy9taXNjX2ljb24ucG5nIzZmNWVhOTc4YjA0ZDAzZTAxOGIzNzlhMmJhYzRjMTBiNWE4ZmUwY2Q1ZTZlMTVjODg4MjhkYzk4NmJlOTZjZmYKLS0tCgp7e2FjY291bnR9fSBpcyBhZGRlZCB0byB0aGUgcmV3YXJkcyBzaGFyaW5nIGxpc3Qgd2l0aCB3ZWlnaHQge3t3ZWlnaHR9fS4AAAAAAOlMRAVjbGFpbfYCLS0tCnNwZWNfdmVyc2lvbjogIjAuMi4wIgp0aXRsZTogQ2xhaW0Kc3VtbWFyeTogJ0NsYWltIHJld2FyZHMgZm9yIHt7bm93cmFwIGFjY291bnR9fScKaWNvbjogaHR0cHM6Ly9hbG1vc3QuZGlnaXRhbC9pbWFnZXMvY2xhaW1faWNvbi5wbmcjYmI1OTdmNGFjYzEzMDU5MjU5MTJlMThlN2I0Y2Y3MDhkMWZhZWMyYWE4OGI3YTUzZDg3OTY5ZTA0NTE2OGVjZgotLS0KCnt7I2lmX2hhc192YWx1ZSBhbW91bnR9fQogICAge3thY2NvdW50fX0gY2xhaW1zIHt7YW1vdW50fX0gZnJvbSB0aGVpciByZXdhcmRzIGJhbGFuY2UuCnt7ZWxzZX19CiAgICB7e2FjY291bnR9fSBjbGFpbXMgdGhlaXIgZW50aXJlIHJld2FyZHMgYmFsYW5jZS4Ke3svaWZfaGFzX3ZhbHVlfX0AAFBXM7cmRQljb25maWd1cmUAAAAA4Cqso0oHZGVsdXNlcsQCLS0tCnNwZWNfdmVyc2lvbjogIjAuMi4wIgp0aXRsZTogRGVsZXRlIHVzZXIKc3VtbWFyeTogJ0RlbGV0ZSB1c2VyIHt7bm93cmFwIGFjY291bnR9fScKaWNvbjogaHR0cHM6Ly9hbG1vc3QuZGlnaXRhbC9pbWFnZXMvbWlzY19pY29uLnBuZyM2ZjVlYTk3OGIwNGQwM2UwMThiMzc5YTJiYWM0YzEwYjVhOGZlMGNkNWU2ZTE1Yzg4ODI4ZGM5ODZiZTk2Y2ZmCi0tLQoKe3thY2NvdW50fX0gaXMgaXMgcmVtb3ZlZCBmcm9tIHRoZSByZXdhcmRzIHNoYXJpbmcgbGlzdC4KClVzZXJzIGNhbiBvbmx5IGJlIHJlbW92ZWQgaWYgdGhlaXIgcmV3YXJkcyBiYWxhbmNlIGlzIHplcm8uAAAAIFenkLoHcmVjZWlwdAAAwFVYq2xS1Qp1cGRhdGV1c2VygAItLS0Kc3BlY192ZXJzaW9uOiAiMC4yLjAiCnRpdGxlOiBVcGRhdGUgdXNlcgpzdW1tYXJ5OiAnVXBkYXRlIHVzZXIge3tub3dyYXAgYWNjb3VudH19JwppY29uOiBodHRwczovL2FsbW9zdC5kaWdpdGFsL2ltYWdlcy9taXNjX2ljb24ucG5nIzZmNWVhOTc4YjA0ZDAzZTAxOGIzNzlhMmJhYzRjMTBiNWE4ZmUwY2Q1ZTZlMTVjODg4MjhkYzk4NmJlOTZjZmYKLS0tCgp7e2FjY291bnR9fSBpcyB1cGRhdGVkIHRvIGhhdmUgd2VpZ2h0IHt7d2VpZ2h0fX0uAgAAAAAwtyZFA2k2NAAABmNvbmZpZwAAAAAAfBXWA2k2NAAACHVzZXJfcm93AAAAAA==' ) export const abi = ABI.from(abiBlob) + export class Contract extends BaseContract { + constructor(args: PartialBy) { + super({ + client: args.client, + abi: abi, + account: Name.from('rewards.gm'), + }) + } + action( + name: T, + data: ActionNameParams[T], + options?: ActionOptions + ): Action { + return super.action(name, data, options) + } + table(name: T) { + return super.table(name, TableMap[name]) + } + } + export interface ActionNameParams { + adduser: ActionParams.Adduser + claim: ActionParams.Claim + configure: ActionParams.Configure + deluser: ActionParams.Deluser + receipt: ActionParams.Receipt + updateuser: ActionParams.Updateuser + } export namespace ActionParams { - export interface adduser { + export interface Adduser { account: NameType weight: UInt16Type } - export interface claim { + export interface Claim { account: NameType - amount: AssetType + amount?: AssetType } - export interface configure { + export interface Configure { token_symbol: Symbol oracle_account: NameType oracle_pairs: Types.OraclePair[] } - export interface deluser { + export interface Deluser { account: NameType } - export interface receipt { + export interface Receipt { account: NameType amount: AssetType ticker: Types.PriceInfo[] } - export interface updateuser { + export interface Updateuser { account: NameType weight: UInt16Type } } - export class Contract extends BaseContract { - constructor(args: PartialBy) { - super({ - client: args.client, - abi: abi, - account: Name.from('rewards.gm'), - }) - } - action( - name: T, - data: ActionNameParams, - options?: ActionOptions - ): Action { - return super.action(name, data, options) - } - } export namespace Types { @Struct.type('adduser') export class Adduser extends Struct { @@ -89,8 +100,8 @@ export namespace RewardsGm { token_symbol!: Asset.Symbol @Struct.field(Name) oracle_account!: Name - @Struct.field(Types.OraclePair, {array: true}) - oracle_pairs!: Types.OraclePair[] + @Struct.field(OraclePair, {array: true}) + oracle_pairs!: OraclePair[] } @Struct.type('configure') export class Configure extends Struct { @@ -98,8 +109,8 @@ export namespace RewardsGm { token_symbol!: Asset.Symbol @Struct.field(Name) oracle_account!: Name - @Struct.field(Types.OraclePair, {array: true}) - oracle_pairs!: Types.OraclePair[] + @Struct.field(OraclePair, {array: true}) + oracle_pairs!: OraclePair[] } @Struct.type('deluser') export class Deluser extends Struct { @@ -121,8 +132,8 @@ export namespace RewardsGm { account!: Name @Struct.field(Asset) amount!: Asset - @Struct.field(Types.PriceInfo, {array: true}) - ticker!: Types.PriceInfo[] + @Struct.field(PriceInfo, {array: true}) + ticker!: PriceInfo[] } @Struct.type('updateuser') export class Updateuser extends Struct { @@ -141,4 +152,9 @@ export namespace RewardsGm { balance!: Asset } } + const TableMap = { + config: Types.Config, + users: Types.UserRow, + } } +export default RewardsGm