Skip to content

Commit

Permalink
All system contract compatibility and better tests (#52)
Browse files Browse the repository at this point in the history
* Rewrote tests to remove recursion and throw

* removed try/catch

* Fixed issue with Symbol typing

* Switching imports to wharfkit/antelope

* catching `Bool` type

* Dynamically import all core types related to ABIs

* Adding missing types

* Fixing TableNames key stringify

* Updating tests to support many contracts

* Changing imports over to wharfkit/antelope instead of session

* Removed notes

* Only override `action` and `table` call if data for those exist

* Scopes should accept generic numbers and cast to strings

Fixes #53

* Fixed scope logic

It was casting undefined to a string and never using `this.account`

* Allow passing `scope` as 2nd param to `contract.table`

This matches the syntax used in the CDT, as discussed here:

https://github.com/orgs/wharfkit/discussions/5#discussioncomment-6515701

* Version 0.4.1

* chore: renamed EOSIO_CORE variables

* refactor: added helper folder

* enhancement: making sure that Name is always in contract imports

* fix: removing $ from field name when applicable

* chore: added full eosio abi

* cleanup: removed contracts folder

* fix: parsing data types properly

* fix: using proper eosio abi

* fix: handling variant and alias types

* fix: handling booleans properly

* fix: handling variants in external and internal types

* cleanup: removed redundant Types namespace from Struct field type

* style: linted

---------

Co-authored-by: dafuga <[email protected]>
  • Loading branch information
aaroncox and dafuga committed Aug 26, 2023
1 parent 17a4b85 commit d1b2afd
Show file tree
Hide file tree
Showing 28 changed files with 6,050 additions and 1,044 deletions.
805 changes: 0 additions & 805 deletions contracts/decentiumorg.ts

This file was deleted.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/contract",
"description": "ContractKit for Wharf",
"version": "0.4.0",
"version": "0.4.1",
"homepage": "https://github.com/wharfkit/contract",
"license": "BSD-3-Clause",
"main": "lib/contract.js",
Expand Down
16 changes: 13 additions & 3 deletions src/codegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,21 @@ export async function codegen(contractName, abi) {
'@wharfkit/contract'
)

const sessionImports = ['ABI', 'Action', 'Blob', 'Struct', ...getCoreImports(abi)]
const allAntelopeImports = [
'ABI',
'Action',
'Blob',
'Struct',
'Name',
...getCoreImports(abi),
]
const antelopeImports = allAntelopeImports.filter(
(item, index) => allAntelopeImports.indexOf(item) === index
)

sessionImports.sort()
antelopeImports.sort()

const importCoreStatement = generateImportStatement(sessionImports, '@wharfkit/session')
const importCoreStatement = generateImportStatement(antelopeImports, '@wharfkit/antelope')

const {classDeclaration} = await generateContractClass(contractName, abi)

Expand Down
27 changes: 21 additions & 6 deletions src/codegen/contract.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {ABI} from '@wharfkit/session'
import {ABI} from '@wharfkit/antelope'
import * as ts from 'typescript'

import {generateClassDeclaration} from './helpers'
Expand Down Expand Up @@ -43,13 +43,17 @@ export async function generateContractClass(contractName: string, abi: ABI.Def)

classMembers.push(constructorMember)

const actionMethod = generateActionMethod(abi)
if (abi.actions.length) {
const actionMethod = generateActionMethod(abi)

classMembers.push(actionMethod)
classMembers.push(actionMethod)
}

const tableMethod = generateTableMethod(abi)
if (abi.tables.length) {
const tableMethod = generateTableMethod(abi)

classMembers.push(tableMethod)
classMembers.push(tableMethod)
}

// Construct class declaration
const classDeclaration = generateClassDeclaration('Contract', classMembers, {
Expand Down Expand Up @@ -189,6 +193,16 @@ function generateTableMethod(abi: ABI.Def): ts.MethodDeclaration {
undefined
)

const scopeParameter = ts.factory.createParameterDeclaration(
undefined,
undefined,
undefined,
'scope',
ts.factory.createToken(ts.SyntaxKind.QuestionToken),
ts.factory.createTypeReferenceNode('NameType'),
undefined
)

// 4. Generate the function body.
const methodBody = ts.factory.createBlock(
[
Expand All @@ -201,6 +215,7 @@ function generateTableMethod(abi: ABI.Def): ts.MethodDeclaration {
undefined,
[
ts.factory.createIdentifier('name'),
ts.factory.createIdentifier('scope'),
ts.factory.createIdentifier('TableMap[name]'),
]
)
Expand All @@ -216,7 +231,7 @@ function generateTableMethod(abi: ABI.Def): ts.MethodDeclaration {
'table',
undefined,
[typeParameter],
[nameParameter],
[nameParameter, scopeParameter],
undefined,
methodBody
)
Expand Down
98 changes: 53 additions & 45 deletions src/codegen/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,19 @@
import * as Antelope from '@wharfkit/antelope'
import {ABI} from '@wharfkit/antelope'
import * as ts from 'typescript'

import {capitalize} from '../utils'

export const EOSIO_CORE_CLASSES = [
'Asset',
'Checksum256',
'Float64',
'Name',
'TimePoint',
'TimePointSec',
'UInt128',
'UInt16',
'UInt32',
'UInt64',
'UInt8',
]

export const EOSIO_CORE_TYPES = [
'AssetType',
'Checksum256Type',
'Float64Type',
'NameType',
'TimePointType',
'UInt128Type',
'UInt16Type',
'UInt32Type',
'UInt64Type',
'UInt8Type',
]
const ANTELOPE_CLASSES: string[] = []
Object.keys(Antelope).map((key) => {
if (Antelope[key].abiName) {
ANTELOPE_CLASSES.push(key)
}
})

export const ANTELOPE_CLASS_MAPPINGS = {
block_timestamp_type: 'BlockTimestamp',
}

export function getCoreImports(abi: ABI.Def) {
const coreImports: string[] = []
Expand Down Expand Up @@ -127,11 +112,18 @@ export function generateInterface(
}

export function findCoreClass(type: string): string | undefined {
for (const coreType of EOSIO_CORE_CLASSES) {
if (type.split('_').join('') === coreType.toLowerCase()) {
return coreType
}
if (ANTELOPE_CLASS_MAPPINGS[type]) {
return ANTELOPE_CLASS_MAPPINGS[type]
}

const parsedType = parseType(type).split('_').join('')

return (
ANTELOPE_CLASSES.find((antelopeClass) => parsedType === antelopeClass.toLowerCase()) ||
ANTELOPE_CLASSES.find(
(antelopeClass) => parsedType.replace(/[0-9]/g, '') === antelopeClass.toLowerCase()
)
)
}

export function findCoreType(type: string): string | undefined {
Expand All @@ -145,26 +137,22 @@ export function findCoreType(type: string): string | undefined {
export function findInternalType(type: string, typeNamespace: string | null, abi: ABI.Def): string {
let {type: typeString} = extractDecorator(type)

typeString = parseType(typeString)

const relevantAbitype = findAbiType(typeString, abi)

if (relevantAbitype) {
typeString = relevantAbitype
}

const variantType = findVariantType(typeString, typeNamespace, abi)

if (variantType) {
typeString = variantType
}

return formatInternalType(typeString, typeNamespace, abi)
}

function formatInternalType(typeString: string, namespace: string | null, abi: ABI.Def): string {
const structNames = abi.structs.map((struct) => struct.name.toLowerCase())

if (structNames.includes(typeString.toLowerCase())) {
return `${namespace ? `${namespace}.` : ''}${generateStructClassName(typeString)}`
return `${namespace ? `${namespace}` : ''}${generateStructClassName(typeString)}`
} else {
return findCoreClass(typeString) || capitalize(typeString)
}
Expand All @@ -177,6 +165,12 @@ export function generateStructClassName(name) {
.join('')
}

function findAliasType(typeString: string, abi: ABI.Def): string | undefined {
const alias = abi.types.find((type) => type.new_type_name === typeString)

return alias?.type
}

function findVariantType(
typeString: string,
namespace: string | null,
Expand All @@ -190,16 +184,24 @@ function findVariantType(
return
}

return abiVariant.types
.map((variant) => formatInternalType(variant, namespace, abi))
.join(' | ')
return abiVariant.types.join(' | ')
}

export function findAbiType(
typeString: string,
abi: ABI.Def,
typeNamespace = ''
): string | undefined {
export function findAbiType(type: string, abi: ABI.Def, typeNamespace = ''): string | undefined {
let typeString = type

const aliasType = findAliasType(typeString, abi)

if (aliasType) {
typeString = aliasType
}

const variantType = findVariantType(typeString, typeNamespace, abi)

if (variantType) {
typeString = variantType
}

const abiType = abi.structs.find((abiType) => abiType.name === typeString)?.name

if (abiType) {
Expand All @@ -211,6 +213,8 @@ export function findExternalType(type: string, abi: ABI.Def, typeNamespace?: str
let {type: typeString} = extractDecorator(type)
const {decorator} = extractDecorator(type)

typeString = parseType(typeString)

const relevantAbitype = findAbiType(typeString, abi, typeNamespace)

if (relevantAbitype) {
Expand All @@ -232,3 +236,7 @@ export function extractDecorator(type: string): {type: string; decorator?: strin

return {type}
}

function parseType(type: string): string {
return type.replace('$', '')
}
29 changes: 24 additions & 5 deletions src/codegen/interfaces.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {ABI} from '@wharfkit/session'
import {ABI} from '@wharfkit/antelope'
import ts from 'typescript'
import {capitalize} from '../utils'
import {findExternalType} from './helpers'
Expand Down Expand Up @@ -29,16 +29,17 @@ export function generateActionNamesInterface(abi: ABI.Def): ts.InterfaceDeclarat

export function generateActionInterface(actionStruct, abi): ts.InterfaceDeclaration {
const members = actionStruct.fields.map((field) => {
const typeReferenceNode = ts.factory.createTypeReferenceNode(
findParamTypeString(field.type, 'Types.', abi)
)

return ts.factory.createPropertySignature(
undefined,
field.name.toLowerCase(),
field.type.includes('?')
? ts.factory.createToken(ts.SyntaxKind.QuestionToken)
: undefined,
ts.factory.createTypeReferenceNode(
findExternalType(field.type, abi, 'Types.'),
undefined
)
typeReferenceNode
)
})

Expand Down Expand Up @@ -68,3 +69,21 @@ export function generateActionsNamespace(abi: ABI.Def): ts.ModuleDeclaration {
ts.NodeFlags.Namespace
)
}

function findParamTypeString(typeString: string, namespace: string | null, abi: ABI.Def): string {
const fieldType = findExternalType(typeString, abi, namespace ? namespace : undefined)

if (fieldType === 'Symbol') {
return 'Asset.SymbolType'
}

if (fieldType === 'Bool') {
return 'boolean'
}

if (['String', 'Boolean', 'Number'].includes(fieldType)) {
return fieldType.toLowerCase()
}

return fieldType
}
4 changes: 2 additions & 2 deletions src/codegen/maps.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import {ABI} from '@wharfkit/session'
import {ABI} from '@wharfkit/antelope'
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),
JSON.stringify(table.name),
ts.factory.createIdentifier(findAbiType(table.type, abi, 'Types.') || table.type)
)
)
Expand Down
41 changes: 28 additions & 13 deletions src/codegen/structs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,20 +157,9 @@ export function generateField(

function orderStructs(structs) {
const orderedStructs: StructData[] = []
const structNames = structs.map((struct) => struct.structName)

for (const struct of structs) {
for (const field of struct.fields) {
const {type: fieldType} = extractDecorator(field.type)

if (structNames.includes(fieldType.toLowerCase())) {
const dependencyStruct = structs.find(
(struct) => struct.structName === fieldType.toLowerCase()
)
orderedStructs.push(dependencyStruct)
}
}

orderedStructs.push(...findDependencies(struct, structs))
orderedStructs.push(struct)
}

Expand All @@ -179,6 +168,28 @@ function orderStructs(structs) {
})
}

function findDependencies(struct: StructData, allStructs: StructData[]): StructData[] {
const dependencies: StructData[] = []

const structNames = allStructs.map((struct) => struct.structName)

for (const field of struct.fields) {
const {type: fieldType} = extractDecorator(field.type)

if (structNames.includes(fieldType.toLowerCase())) {
const dependencyStruct = allStructs.find(
(struct) => struct.structName === fieldType.toLowerCase()
)
if (dependencyStruct) {
dependencies.push(...findDependencies(dependencyStruct, allStructs))
dependencies.push(dependencyStruct)
}
}
}

return dependencies
}

function findFieldStructType(
typeString: string,
namespace: string | null,
Expand All @@ -200,10 +211,14 @@ function findFieldStructTypeString(
): string {
const fieldType = findInternalType(typeString, namespace, abi)

if (['String', 'Boolean', 'Number'].includes(fieldType)) {
if (['String', 'Number'].includes(fieldType)) {
return fieldType.toLowerCase()
}

if (fieldType === 'Bool') {
return 'boolean'
}

if (fieldType === 'Symbol') {
return 'Asset.Symbol'
}
Expand Down
2 changes: 1 addition & 1 deletion src/contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export class Contract {
return this.tableNames.includes(String(name))
}

public table<RowType = any>(name: NameType, rowType?): Table<RowType> {
public table<RowType = any>(name: NameType, scope?: NameType, rowType?): Table<RowType> {
if (!this.hasTable(name)) {
throw new Error(`Contract (${this.account}) does not have a table named (${name})`)
}
Expand Down
Loading

0 comments on commit d1b2afd

Please sign in to comment.