From 1ce66b4a81b736f5288463be609a902af64cbe77 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Tue, 5 Dec 2017 19:53:59 +0300 Subject: Rename abi-gen to typed-contracts --- packages/0x.js/package.json | 6 +- .../src/contract_wrappers/generated/ether_token.ts | 4 +- .../src/contract_wrappers/generated/exchange.ts | 4 +- .../0x.js/src/contract_wrappers/generated/token.ts | 4 +- .../contract_wrappers/generated/token_registry.ts | 4 +- .../generated/token_transfer_proxy.ts | 4 +- packages/abi-gen-templates/contract.mustache | 27 ++++++ packages/abi-gen-templates/package.json | 14 ++++ packages/abi-gen-templates/partials/call.mustache | 15 ++++ .../abi-gen-templates/partials/params.mustache | 3 + .../partials/return_type.mustache | 6 ++ packages/abi-gen-templates/partials/tx.mustache | 51 +++++++++++ .../partials/typed_params.mustache | 3 + packages/abi-gen/README.md | 38 +++++++++ packages/abi-gen/package.json | 48 +++++++++++ packages/abi-gen/src/globals.d.ts | 4 + packages/abi-gen/src/index.ts | 98 ++++++++++++++++++++++ packages/abi-gen/src/types.ts | 15 ++++ packages/abi-gen/src/utils.ts | 56 +++++++++++++ packages/abi-gen/tsconfig.json | 17 ++++ packages/abi-gen/tslint.json | 5 ++ .../typed-contracts-templates/contract.mustache | 27 ------ packages/typed-contracts-templates/package.json | 14 ---- .../partials/call.mustache | 15 ---- .../partials/params.mustache | 3 - .../partials/return_type.mustache | 6 -- .../typed-contracts-templates/partials/tx.mustache | 51 ----------- .../partials/typed_params.mustache | 3 - packages/typed-contracts/README.md | 38 --------- packages/typed-contracts/package.json | 48 ----------- packages/typed-contracts/src/globals.d.ts | 4 - packages/typed-contracts/src/index.ts | 98 ---------------------- packages/typed-contracts/src/types.ts | 15 ---- packages/typed-contracts/src/utils.ts | 56 ------------- packages/typed-contracts/tsconfig.json | 17 ---- packages/typed-contracts/tslint.json | 5 -- 36 files changed, 413 insertions(+), 413 deletions(-) create mode 100644 packages/abi-gen-templates/contract.mustache create mode 100644 packages/abi-gen-templates/package.json create mode 100644 packages/abi-gen-templates/partials/call.mustache create mode 100644 packages/abi-gen-templates/partials/params.mustache create mode 100644 packages/abi-gen-templates/partials/return_type.mustache create mode 100644 packages/abi-gen-templates/partials/tx.mustache create mode 100644 packages/abi-gen-templates/partials/typed_params.mustache create mode 100644 packages/abi-gen/README.md create mode 100644 packages/abi-gen/package.json create mode 100644 packages/abi-gen/src/globals.d.ts create mode 100644 packages/abi-gen/src/index.ts create mode 100644 packages/abi-gen/src/types.ts create mode 100644 packages/abi-gen/src/utils.ts create mode 100644 packages/abi-gen/tsconfig.json create mode 100644 packages/abi-gen/tslint.json delete mode 100644 packages/typed-contracts-templates/contract.mustache delete mode 100644 packages/typed-contracts-templates/package.json delete mode 100644 packages/typed-contracts-templates/partials/call.mustache delete mode 100644 packages/typed-contracts-templates/partials/params.mustache delete mode 100644 packages/typed-contracts-templates/partials/return_type.mustache delete mode 100644 packages/typed-contracts-templates/partials/tx.mustache delete mode 100644 packages/typed-contracts-templates/partials/typed_params.mustache delete mode 100644 packages/typed-contracts/README.md delete mode 100644 packages/typed-contracts/package.json delete mode 100644 packages/typed-contracts/src/globals.d.ts delete mode 100644 packages/typed-contracts/src/index.ts delete mode 100644 packages/typed-contracts/src/types.ts delete mode 100644 packages/typed-contracts/src/utils.ts delete mode 100644 packages/typed-contracts/tsconfig.json delete mode 100644 packages/typed-contracts/tslint.json diff --git a/packages/0x.js/package.json b/packages/0x.js/package.json index b91f23ecb..b09aadd7e 100644 --- a/packages/0x.js/package.json +++ b/packages/0x.js/package.json @@ -16,7 +16,7 @@ "build": "run-p build:umd:prod build:commonjs; exit 0;", "docs:json": "typedoc --excludePrivate --excludeExternals --target ES5 --json $JSON_FILE_PATH $PROJECT_DIR", "upload_docs_json": "aws s3 cp generated_docs/index.json $S3_URL --profile 0xproject --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers --content-type application/json", - "generate_contract_wrappers": "typed-contracts --abiGlob 'src/artifacts/@(Exchange|Token|TokenTransferProxy|EtherToken|TokenRegistry).json' --templates ../typed-contracts-templates/ --output src/contract_wrappers/generated", + "generate_contract_wrappers": "abi-gen --abiGlob 'src/artifacts/@(Exchange|Token|TokenTransferProxy|EtherToken|TokenRegistry).json' --templates ../abi-gen-templates/ --output src/contract_wrappers/generated", "lint": "tslint --project . 'src/**/*.ts' 'test/**/*.ts'", "test:circleci": "run-s test:coverage report_test_coverage && if [ $CIRCLE_BRANCH = \"development\" ]; then yarn test:umd; fi", "test": "run-s clean test:commonjs", @@ -46,8 +46,8 @@ }, "devDependencies": { "@0xproject/tslint-config": "^0.2.0", - "@0xproject/typed-contracts": "^0.0.0", - "@0xproject/typed-contracts-templates": "^0.0.0", + "abi-gen": "^0.0.0", + "abi-gen-templates": "^0.0.0", "@types/bintrees": "^1.0.2", "@types/jsonschema": "^1.1.1", "@types/lodash": "^4.14.64", diff --git a/packages/0x.js/src/contract_wrappers/generated/ether_token.ts b/packages/0x.js/src/contract_wrappers/generated/ether_token.ts index aae6230c9..1e4230788 100644 --- a/packages/0x.js/src/contract_wrappers/generated/ether_token.ts +++ b/packages/0x.js/src/contract_wrappers/generated/ether_token.ts @@ -1,6 +1,6 @@ /** - * This file is auto-generated using @0xproject/typed-contracts. Don't edit directly. - * Templates can be found at https://github.com/0xProject/0x.js/tree/development/packages/typed-contracts-templates. + * This file is auto-generated using @0xproject/abi-gen. Don't edit directly. + * Templates can be found at https://github.com/0xProject/0x.js/tree/development/packages/abi-gen-templates. */ import {BigNumber} from 'bignumber.js'; import * as Web3 from 'web3'; diff --git a/packages/0x.js/src/contract_wrappers/generated/exchange.ts b/packages/0x.js/src/contract_wrappers/generated/exchange.ts index 512cc80d7..e6bc74c65 100644 --- a/packages/0x.js/src/contract_wrappers/generated/exchange.ts +++ b/packages/0x.js/src/contract_wrappers/generated/exchange.ts @@ -1,6 +1,6 @@ /** - * This file is auto-generated using @0xproject/typed-contracts. Don't edit directly. - * Templates can be found at https://github.com/0xProject/0x.js/tree/development/packages/typed-contracts-templates. + * This file is auto-generated using @0xproject/abi-gen. Don't edit directly. + * Templates can be found at https://github.com/0xProject/0x.js/tree/development/packages/abi-gen-templates. */ import {BigNumber} from 'bignumber.js'; import * as Web3 from 'web3'; diff --git a/packages/0x.js/src/contract_wrappers/generated/token.ts b/packages/0x.js/src/contract_wrappers/generated/token.ts index f8f578dd9..7762a29e5 100644 --- a/packages/0x.js/src/contract_wrappers/generated/token.ts +++ b/packages/0x.js/src/contract_wrappers/generated/token.ts @@ -1,6 +1,6 @@ /** - * This file is auto-generated using @0xproject/typed-contracts. Don't edit directly. - * Templates can be found at https://github.com/0xProject/0x.js/tree/development/packages/typed-contracts-templates. + * This file is auto-generated using @0xproject/abi-gen. Don't edit directly. + * Templates can be found at https://github.com/0xProject/0x.js/tree/development/packages/abi-gen-templates. */ import {BigNumber} from 'bignumber.js'; import * as Web3 from 'web3'; diff --git a/packages/0x.js/src/contract_wrappers/generated/token_registry.ts b/packages/0x.js/src/contract_wrappers/generated/token_registry.ts index 5638a92d6..ce03449d8 100644 --- a/packages/0x.js/src/contract_wrappers/generated/token_registry.ts +++ b/packages/0x.js/src/contract_wrappers/generated/token_registry.ts @@ -1,6 +1,6 @@ /** - * This file is auto-generated using @0xproject/typed-contracts. Don't edit directly. - * Templates can be found at https://github.com/0xProject/0x.js/tree/development/packages/typed-contracts-templates. + * This file is auto-generated using @0xproject/abi-gen. Don't edit directly. + * Templates can be found at https://github.com/0xProject/0x.js/tree/development/packages/abi-gen-templates. */ import {BigNumber} from 'bignumber.js'; import * as Web3 from 'web3'; diff --git a/packages/0x.js/src/contract_wrappers/generated/token_transfer_proxy.ts b/packages/0x.js/src/contract_wrappers/generated/token_transfer_proxy.ts index 33e244d8d..41c9669e3 100644 --- a/packages/0x.js/src/contract_wrappers/generated/token_transfer_proxy.ts +++ b/packages/0x.js/src/contract_wrappers/generated/token_transfer_proxy.ts @@ -1,6 +1,6 @@ /** - * This file is auto-generated using @0xproject/typed-contracts. Don't edit directly. - * Templates can be found at https://github.com/0xProject/0x.js/tree/development/packages/typed-contracts-templates. + * This file is auto-generated using @0xproject/abi-gen. Don't edit directly. + * Templates can be found at https://github.com/0xProject/0x.js/tree/development/packages/abi-gen-templates. */ import {BigNumber} from 'bignumber.js'; import * as Web3 from 'web3'; diff --git a/packages/abi-gen-templates/contract.mustache b/packages/abi-gen-templates/contract.mustache new file mode 100644 index 000000000..27783fb6e --- /dev/null +++ b/packages/abi-gen-templates/contract.mustache @@ -0,0 +1,27 @@ +/** + * This file is auto-generated using abi-gen. Don't edit directly. + * Templates can be found at https://github.com/0xProject/0x.js/tree/development/packages/abi-gen-templates. + */ +import {BigNumber} from 'bignumber.js'; +import * as Web3 from 'web3'; + +import {TxData, TxDataPayable} from '../../types'; +import {classUtils} from '../../utils/class_utils'; +import {promisify} from '../../utils/promisify'; + +import {BaseContract} from './base_contract'; + +export class {{contractName}}Contract extends BaseContract { +{{#each methods}} + {{#this.constant}} + {{> call contractName=../contractName}} + {{/this.constant}} + {{^this.constant}} + {{> tx contractName=../contractName}} + {{/this.constant}} +{{/each}} + constructor(web3ContractInstance: Web3.ContractInstance, defaults: Partial) { + super(web3ContractInstance, defaults); + classUtils.bindAll(this, ['web3ContractInstance', 'defaults']); + } +} // tslint:disable:max-file-line-count diff --git a/packages/abi-gen-templates/package.json b/packages/abi-gen-templates/package.json new file mode 100644 index 000000000..622c35478 --- /dev/null +++ b/packages/abi-gen-templates/package.json @@ -0,0 +1,14 @@ +{ + "name": "@0xproject/abi-gen-templates", + "version": "0.0.0", + "description": "Handlebars templates to generate TS contract wrappers", + "repository": { + "type": "git", + "url": "https://github.com/0xProject/0x.js.git" + }, + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/0xProject/0x.js/issues" + }, + "homepage": "https://github.com/0xProject/0x.js/packages/abi-gen-templates/README.md" +} diff --git a/packages/abi-gen-templates/partials/call.mustache b/packages/abi-gen-templates/partials/call.mustache new file mode 100644 index 000000000..ef4bda724 --- /dev/null +++ b/packages/abi-gen-templates/partials/call.mustache @@ -0,0 +1,15 @@ +public {{this.name}} = { + async callAsync( + {{> typed_params inputs=inputs}} + defaultBlock?: Web3.BlockParam, + ): Promise<{{> return_type outputs=outputs}}> { + const self = this as {{contractName}}Contract; + const result = await promisify<{{> return_type outputs=outputs}}>( + self.web3ContractInstance.{{this.name}}.call, + self.web3ContractInstance, + )( + {{> params inputs=inputs}} + ); + return result; + }, +}; diff --git a/packages/abi-gen-templates/partials/params.mustache b/packages/abi-gen-templates/partials/params.mustache new file mode 100644 index 000000000..ac5d4ae85 --- /dev/null +++ b/packages/abi-gen-templates/partials/params.mustache @@ -0,0 +1,3 @@ +{{#each inputs}} +{{name}}, +{{/each}} diff --git a/packages/abi-gen-templates/partials/return_type.mustache b/packages/abi-gen-templates/partials/return_type.mustache new file mode 100644 index 000000000..383961a40 --- /dev/null +++ b/packages/abi-gen-templates/partials/return_type.mustache @@ -0,0 +1,6 @@ +{{#singleReturnValue}} +{{#returnType outputs.0.type}}{{/returnType}} +{{/singleReturnValue}} +{{^singleReturnValue}} +[{{#each outputs}}{{#returnType type}}{{/returnType}}{{#unless @last}}, {{/unless}}{{/each}}] +{{/singleReturnValue}} diff --git a/packages/abi-gen-templates/partials/tx.mustache b/packages/abi-gen-templates/partials/tx.mustache new file mode 100644 index 000000000..8a43e5319 --- /dev/null +++ b/packages/abi-gen-templates/partials/tx.mustache @@ -0,0 +1,51 @@ +public {{this.name}} = { + async sendTransactionAsync( + {{> typed_params inputs=inputs}} + {{#this.payable}} + txData: TxDataPayable = {}, + {{/this.payable}} + {{^this.payable}} + txData: TxData = {}, + {{/this.payable}} + ): Promise { + const self = this as {{contractName}}Contract; + const txDataWithDefaults = await self.applyDefaultsToTxDataAsync( + txData, + self.{{this.name}}.estimateGasAsync.bind( + self, + {{> params inputs=inputs}} + ), + ); + const txHash = await promisify( + self.web3ContractInstance.{{this.name}}, self.web3ContractInstance, + )( + {{> params inputs=inputs}} + txDataWithDefaults, + ); + return txHash; + }, + async estimateGasAsync( + {{> typed_params inputs=inputs}} + txData: TxData = {}, + ): Promise { + const self = this as {{contractName}}Contract; + const txDataWithDefaults = await self.applyDefaultsToTxDataAsync( + txData, + ); + const gas = await promisify( + self.web3ContractInstance.{{this.name}}.estimateGas, self.web3ContractInstance, + )( + {{> params inputs=inputs}} + txDataWithDefaults, + ); + return gas; + }, + getABIEncodedTransactionData( + {{> typed_params inputs=inputs}} + txData: TxData = {}, + ): string { + const self = this as {{contractName}}Contract; + const abiEncodedTransactionData = self.web3ContractInstance.{{this.name}}.getData(); + return abiEncodedTransactionData; + }, +}; diff --git a/packages/abi-gen-templates/partials/typed_params.mustache b/packages/abi-gen-templates/partials/typed_params.mustache new file mode 100644 index 000000000..3ea4b2e95 --- /dev/null +++ b/packages/abi-gen-templates/partials/typed_params.mustache @@ -0,0 +1,3 @@ +{{#each inputs}} + {{name}}: {{#parameterType type}}{{/parameterType}}, +{{/each}} diff --git a/packages/abi-gen/README.md b/packages/abi-gen/README.md new file mode 100644 index 000000000..77dcae2ad --- /dev/null +++ b/packages/abi-gen/README.md @@ -0,0 +1,38 @@ +# Typed contracts + +This package allows you to generate TS contract wrappers from ABI files. +You can write your custom handlebars templates which will allow you to seamlessly integrate the generated code into your existing codebase with existing conventions. + +For the example of the generated [wrapper files](https://github.com/0xProject/0x.js/tree/development/packages/0x.js/src/contract_wrappers/generated) check out 0x.js. +[Here](https://github.com/0xProject/0x.js/tree/development/packages/abi-gen-templates) are the templates used to generate those files. + +## Instalation +`yarn add -g @0xproject/abi-gen` +## Usage +``` +abi-gen +Options: + --help Show help [boolean] + --version Show version number [boolean] + --abiGlob Glob pattern to search for ABI JSON files [string] [required] + --templates Folder where to search for templates [string] [required] + --output Folder where to put the output files [string] [required] +``` +## ABI files +You're required to pass a [glob](https://en.wikipedia.org/wiki/Glob_(programming) template where your abi files are located. +TL;DR - here is the exmaple from 0x.js. + +`--abiGlob 'src/artifacts/@(Exchange|Token|TokenTransferProxy|EtherToken|TokenRegistry).json` + +We could've just used `--abiGlob 'src/artifacts/*.json` but we wanted to exclude some of the abi files. + +The abi file should be either a truffle contract artifact (a JSON object with the abi key) or a JSON abi array. +## How to write custom templates? +The best way to get started is to copy [0x.js templates](https://github.com/0xProject/0x.js/tree/development/packages/abi-gen-templates) and start playing with them adjusting them for your needs. +We use [handlebars](handlebarsjs.com) template engine under the hood. +You need to have a master template called `contract.mustache`. it will be used to generate each contract wrapper. Although - you don't need and probably shouldn't write all your logic in a single template file. You can write [partial templates](http://handlebarsjs.com/partials.html) and as long as they are within partials folder - they will be registered and available. +## Which data/context do I get in my templates? +For now you don't get much on top of methods abi and a contract name cause if was enough for our use-case, but if you need something else - create a PR. +[Type definition](https://github.com/0xProject/0x.js/tree/development/packages/abi-gen/src/types.ts) of what we pass to a render method. +## Output files +Output files will be generated within an output folder with names converted to camel case and taken from abi files names. If you already have some files in that folder they will be overwritten. diff --git a/packages/abi-gen/package.json b/packages/abi-gen/package.json new file mode 100644 index 000000000..01bede249 --- /dev/null +++ b/packages/abi-gen/package.json @@ -0,0 +1,48 @@ +{ + "name": "abi-gen", + "version": "0.0.0", + "description": "Generate contract wrappers from ABI and handlebars templates", + "main": "lib/index.js", + "types": "lib/index.d.ts", + "scripts": { + "lint": "tslint --project . 'src/**/*.ts'", + "clean": "shx rm -rf lib", + "build": "tsc" + }, + "bin": { + "abi-gen": "lib/index.js" + }, + "repository": { + "type": "git", + "url": "https://github.com/0xProject/0x.js.git" + }, + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/0xProject/0x.js/issues" + }, + "homepage": "https://github.com/0xProject/0x.js/packages/abi-gen/README.md", + "dependencies": { + "@types/handlebars": "^4.0.36", + "bignumber.js": "^5.0.0", + "chalk": "^2.3.0", + "glob": "^7.1.2", + "handlebars": "^4.0.11", + "lodash": "^4.17.4", + "mkdirp": "^0.5.1", + "to-snake-case": "^1.0.0", + "web3": "^0.20.0", + "yargs": "^10.0.3" + }, + "devDependencies": { + "@0xproject/tslint-config": "^0.2.0", + "@types/glob": "^5.0.33", + "@types/mkdirp": "^0.5.1", + "@types/node": "^8.0.53", + "@types/yargs": "^8.0.2", + "npm-run-all": "^4.1.1", + "shx": "^0.2.2", + "tslint": "5.8.0", + "typescript": "~2.6.1", + "web3-typescript-typings": "^0.7.2" + } +} diff --git a/packages/abi-gen/src/globals.d.ts b/packages/abi-gen/src/globals.d.ts new file mode 100644 index 000000000..39df3f852 --- /dev/null +++ b/packages/abi-gen/src/globals.d.ts @@ -0,0 +1,4 @@ +declare function toSnakeCase(str: string): string; +declare module 'to-snake-case' { + export = toSnakeCase; +} diff --git a/packages/abi-gen/src/index.ts b/packages/abi-gen/src/index.ts new file mode 100644 index 000000000..19d289e49 --- /dev/null +++ b/packages/abi-gen/src/index.ts @@ -0,0 +1,98 @@ +#!/usr/bin/env node + +import chalk from 'chalk'; +import * as fs from 'fs'; +import {sync as globSync} from 'glob'; +import * as Handlebars from 'handlebars'; +import * as _ from 'lodash'; +import * as mkdirp from 'mkdirp'; +import * as yargs from 'yargs'; + +import toSnakeCase = require('to-snake-case'); +import * as Web3 from 'web3'; + +import {ContextData, ParamKind} from './types'; +import {utils} from './utils'; + +const ABI_TYPE_METHOD = 'function'; +const MAIN_TEMPLATE_NAME = 'contract.mustache'; + +const args = yargs + .option('abiGlob', { + describe: 'Glob pattern to search for ABI JSON files', + type: 'string', + demand: true, + }) + .option('templates', { + describe: 'Folder where to search for templates', + type: 'string', + demand: true, + }) + .option('output', { + describe: 'Folder where to put the output files', + type: 'string', + demand: true, + }) + .argv; + +function writeOutputFile(name: string, renderedTsCode: string): void { + const fileName = toSnakeCase(name); + const filePath = `${args.output}/${fileName}.ts`; + fs.writeFileSync(filePath, renderedTsCode); + utils.log(`Created: ${chalk.bold(filePath)}`); +} + +Handlebars.registerHelper('parameterType', utils.solTypeToTsType.bind(utils, ParamKind.Input)); +Handlebars.registerHelper('returnType', utils.solTypeToTsType.bind(utils, ParamKind.Output)); +const partialTemplateFileNames = globSync(`${args.templates}/partials/**/*.mustache`); +for (const partialTemplateFileName of partialTemplateFileNames) { + const namedContent = utils.getNamedContent(partialTemplateFileName); + Handlebars.registerPartial(namedContent.name, namedContent.content); +} + +const mainTemplate = utils.getNamedContent(`${args.templates}/${MAIN_TEMPLATE_NAME}`); +const template = Handlebars.compile(mainTemplate.content); +const abiFileNames = globSync(args.abiGlob); +if (_.isEmpty(abiFileNames)) { + utils.log(`${chalk.red(`No ABI files found.`)}`); + utils.log(`Please make sure you've passed the correct folder name and that the files have + ${chalk.bold('*.json')} extensions`); + process.exit(1); +} else { + utils.log(`Found ${chalk.green(`${abiFileNames.length}`)} ${chalk.bold('ABI')} files`); + mkdirp.sync(args.output); +} +for (const abiFileName of abiFileNames) { + const namedContent = utils.getNamedContent(abiFileName); + utils.log(`Processing: ${chalk.bold(namedContent.name)}...`); + const parsedContent = JSON.parse(namedContent.content); + const ABI = _.isArray(parsedContent) ? + parsedContent : // ABI file + parsedContent.abi; // Truffle contracts file + if (_.isUndefined(ABI)) { + utils.log(`${chalk.red(`ABI not found in ${abiFileName}.`)}`); + utils.log(`Please make sure your ABI file is either an array with ABI entries or an object with the abi key`); + process.exit(1); + } + const methodAbis = ABI.filter((abi: Web3.AbiDefinition) => abi.type === ABI_TYPE_METHOD) as Web3.MethodAbi[]; + const methodsData = _.map(methodAbis, methodAbi => { + _.map(methodAbi.inputs, input => { + if (_.isEmpty(input.name)) { + // Auto-generated getters don't have parameter names + input.name = 'index'; + } + }); + // This will make templates simpler + const methodData = { + ...methodAbi, + singleReturnValue: methodAbi.outputs.length === 1, + }; + return methodData; + }); + const contextData = { + contractName: namedContent.name, + methods: methodsData, + }; + const renderedTsCode = template(contextData); + writeOutputFile(namedContent.name, renderedTsCode); +} diff --git a/packages/abi-gen/src/types.ts b/packages/abi-gen/src/types.ts new file mode 100644 index 000000000..1dc039c83 --- /dev/null +++ b/packages/abi-gen/src/types.ts @@ -0,0 +1,15 @@ +import * as Web3 from 'web3'; + +export enum ParamKind { + Input = 'input', + Output = 'output', +} + +export interface Method extends Web3.MethodAbi { + singleReturnValue: boolean; +} + +export interface ContextData { + contractName: string; + methods: Method[]; +} diff --git a/packages/abi-gen/src/utils.ts b/packages/abi-gen/src/utils.ts new file mode 100644 index 000000000..eaf5a30cc --- /dev/null +++ b/packages/abi-gen/src/utils.ts @@ -0,0 +1,56 @@ +import * as fs from 'fs'; +import * as _ from 'lodash'; +import * as path from 'path'; + +import {ParamKind} from './types'; + +export const utils = { + solTypeToTsType(paramKind: ParamKind, solType: string): string { + const trailingArrayRegex = /\[\d*\]$/; + if (solType.match(trailingArrayRegex)) { + const arrayItemSolType = solType.replace(trailingArrayRegex, ''); + const arrayItemTsType = utils.solTypeToTsType(paramKind, arrayItemSolType); + const arrayTsType = `${arrayItemTsType}[]`; + return arrayTsType; + } else { + const solTypeRegexToTsType = [ + {regex: '^string$', tsType: 'string'}, + {regex: '^address$', tsType: 'string'}, + {regex: '^bool$', tsType: 'boolean'}, + {regex: '^u?int\\d*$', tsType: 'BigNumber'}, + {regex: '^bytes\\d*$', tsType: 'string'}, + ]; + if (paramKind === ParamKind.Input) { + // web3 allows to pass those an non-bignumbers and that's nice + // but it always returns stuff as BigNumbers + solTypeRegexToTsType.unshift({regex: '^u?int(8|16|32)?$', tsType: 'number|BigNumber'}); + } + for (const regexAndTxType of solTypeRegexToTsType) { + const {regex, tsType} = regexAndTxType; + if (solType.match(regex)) { + return tsType; + } + } + throw new Error(`Unknown Solidity type found: ${solType}`); + } + }, + log(...args: any[]): void { + console.log(...args); // tslint:disable-line:no-console + }, + getPartialNameFromFileName(filename: string): string { + const name = path.parse(filename).name; + return name; + }, + getNamedContent(filename: string): {name: string; content: string} { + const name = utils.getPartialNameFromFileName(filename); + try { + const content = fs.readFileSync(filename).toString(); + return { + name, + content, + }; + } catch (err) { + throw new Error(`Failed to read ${filename}: ${err}`); + } + }, +}; diff --git a/packages/abi-gen/tsconfig.json b/packages/abi-gen/tsconfig.json new file mode 100644 index 000000000..2a3667890 --- /dev/null +++ b/packages/abi-gen/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es5", + "lib": ["es2015", "dom"], + "outDir": "lib", + "sourceMap": true, + "declaration": true, + "noImplicitAny": true, + "strictNullChecks": true + }, + "include": [ + "./src/**/*", + "./test/**/*", + "../../node_modules/web3-typescript-typings/index.d.ts", + ] +} diff --git a/packages/abi-gen/tslint.json b/packages/abi-gen/tslint.json new file mode 100644 index 000000000..a07795151 --- /dev/null +++ b/packages/abi-gen/tslint.json @@ -0,0 +1,5 @@ +{ + "extends": [ + "@0xproject/tslint-config" + ] +} diff --git a/packages/typed-contracts-templates/contract.mustache b/packages/typed-contracts-templates/contract.mustache deleted file mode 100644 index cb3bb3631..000000000 --- a/packages/typed-contracts-templates/contract.mustache +++ /dev/null @@ -1,27 +0,0 @@ -/** - * This file is auto-generated using @0xproject/typed-contracts. Don't edit directly. - * Templates can be found at https://github.com/0xProject/0x.js/tree/development/packages/typed-contracts-templates. - */ -import {BigNumber} from 'bignumber.js'; -import * as Web3 from 'web3'; - -import {TxData, TxDataPayable} from '../../types'; -import {classUtils} from '../../utils/class_utils'; -import {promisify} from '../../utils/promisify'; - -import {BaseContract} from './base_contract'; - -export class {{contractName}}Contract extends BaseContract { -{{#each methods}} - {{#this.constant}} - {{> call contractName=../contractName}} - {{/this.constant}} - {{^this.constant}} - {{> tx contractName=../contractName}} - {{/this.constant}} -{{/each}} - constructor(web3ContractInstance: Web3.ContractInstance, defaults: Partial) { - super(web3ContractInstance, defaults); - classUtils.bindAll(this, ['web3ContractInstance', 'defaults']); - } -} // tslint:disable:max-file-line-count diff --git a/packages/typed-contracts-templates/package.json b/packages/typed-contracts-templates/package.json deleted file mode 100644 index e690e93a1..000000000 --- a/packages/typed-contracts-templates/package.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "@0xproject/typed-contracts-templates", - "version": "0.0.0", - "description": "Handlebars templates to generate TS contract wrappers", - "repository": { - "type": "git", - "url": "https://github.com/0xProject/0x.js.git" - }, - "license": "Apache-2.0", - "bugs": { - "url": "https://github.com/0xProject/0x.js/issues" - }, - "homepage": "https://github.com/0xProject/0x.js/packages/typed-contracts-templates/README.md" -} diff --git a/packages/typed-contracts-templates/partials/call.mustache b/packages/typed-contracts-templates/partials/call.mustache deleted file mode 100644 index ef4bda724..000000000 --- a/packages/typed-contracts-templates/partials/call.mustache +++ /dev/null @@ -1,15 +0,0 @@ -public {{this.name}} = { - async callAsync( - {{> typed_params inputs=inputs}} - defaultBlock?: Web3.BlockParam, - ): Promise<{{> return_type outputs=outputs}}> { - const self = this as {{contractName}}Contract; - const result = await promisify<{{> return_type outputs=outputs}}>( - self.web3ContractInstance.{{this.name}}.call, - self.web3ContractInstance, - )( - {{> params inputs=inputs}} - ); - return result; - }, -}; diff --git a/packages/typed-contracts-templates/partials/params.mustache b/packages/typed-contracts-templates/partials/params.mustache deleted file mode 100644 index ac5d4ae85..000000000 --- a/packages/typed-contracts-templates/partials/params.mustache +++ /dev/null @@ -1,3 +0,0 @@ -{{#each inputs}} -{{name}}, -{{/each}} diff --git a/packages/typed-contracts-templates/partials/return_type.mustache b/packages/typed-contracts-templates/partials/return_type.mustache deleted file mode 100644 index 383961a40..000000000 --- a/packages/typed-contracts-templates/partials/return_type.mustache +++ /dev/null @@ -1,6 +0,0 @@ -{{#singleReturnValue}} -{{#returnType outputs.0.type}}{{/returnType}} -{{/singleReturnValue}} -{{^singleReturnValue}} -[{{#each outputs}}{{#returnType type}}{{/returnType}}{{#unless @last}}, {{/unless}}{{/each}}] -{{/singleReturnValue}} diff --git a/packages/typed-contracts-templates/partials/tx.mustache b/packages/typed-contracts-templates/partials/tx.mustache deleted file mode 100644 index 8a43e5319..000000000 --- a/packages/typed-contracts-templates/partials/tx.mustache +++ /dev/null @@ -1,51 +0,0 @@ -public {{this.name}} = { - async sendTransactionAsync( - {{> typed_params inputs=inputs}} - {{#this.payable}} - txData: TxDataPayable = {}, - {{/this.payable}} - {{^this.payable}} - txData: TxData = {}, - {{/this.payable}} - ): Promise { - const self = this as {{contractName}}Contract; - const txDataWithDefaults = await self.applyDefaultsToTxDataAsync( - txData, - self.{{this.name}}.estimateGasAsync.bind( - self, - {{> params inputs=inputs}} - ), - ); - const txHash = await promisify( - self.web3ContractInstance.{{this.name}}, self.web3ContractInstance, - )( - {{> params inputs=inputs}} - txDataWithDefaults, - ); - return txHash; - }, - async estimateGasAsync( - {{> typed_params inputs=inputs}} - txData: TxData = {}, - ): Promise { - const self = this as {{contractName}}Contract; - const txDataWithDefaults = await self.applyDefaultsToTxDataAsync( - txData, - ); - const gas = await promisify( - self.web3ContractInstance.{{this.name}}.estimateGas, self.web3ContractInstance, - )( - {{> params inputs=inputs}} - txDataWithDefaults, - ); - return gas; - }, - getABIEncodedTransactionData( - {{> typed_params inputs=inputs}} - txData: TxData = {}, - ): string { - const self = this as {{contractName}}Contract; - const abiEncodedTransactionData = self.web3ContractInstance.{{this.name}}.getData(); - return abiEncodedTransactionData; - }, -}; diff --git a/packages/typed-contracts-templates/partials/typed_params.mustache b/packages/typed-contracts-templates/partials/typed_params.mustache deleted file mode 100644 index 3ea4b2e95..000000000 --- a/packages/typed-contracts-templates/partials/typed_params.mustache +++ /dev/null @@ -1,3 +0,0 @@ -{{#each inputs}} - {{name}}: {{#parameterType type}}{{/parameterType}}, -{{/each}} diff --git a/packages/typed-contracts/README.md b/packages/typed-contracts/README.md deleted file mode 100644 index 5649af0dd..000000000 --- a/packages/typed-contracts/README.md +++ /dev/null @@ -1,38 +0,0 @@ -# Typed contracts - -This package allows you to generate TS contract wrappers from ABI files. -You can write your custom handlebars templates which will allow you to seamlessly integrate the generated code into your existing codebase with existing conventions. - -For the example of the generated [wrapper files](https://github.com/0xProject/0x.js/tree/development/packages/0x.js/src/contract_wrappers/generated) check out 0x.js. -[Here](https://github.com/0xProject/0x.js/tree/development/packages/typed-contracts-templates) are the templates used to generate those files. - -## Instalation -`yarn add -g @0xproject/typed-contracts` -## Usage -``` -typed-contracts -Options: - --help Show help [boolean] - --version Show version number [boolean] - --abiGlob Glob pattern to search for ABI JSON files [string] [required] - --templates Folder where to search for templates [string] [required] - --output Folder where to put the output files [string] [required] -``` -## ABI files -You're required to pass a [glob](https://en.wikipedia.org/wiki/Glob_(programming) template where your abi files are located. -TL;DR - here is the exmaple from 0x.js. - -`--abiGlob 'src/artifacts/@(Exchange|Token|TokenTransferProxy|EtherToken|TokenRegistry).json` - -We could've just used `--abiGlob 'src/artifacts/*.json` but we wanted to exclude some of the abi files. - -The abi file should be either a truffle contract artifact (a JSON object with the abi key) or a JSON abi array. -## How to write custom templates? -The best way to get started is to copy [0x.js templates](https://github.com/0xProject/0x.js/tree/development/packages/typed-contracts-templates) and start playing with them adjusting them for your needs. -We use [handlebars](handlebarsjs.com) template engine under the hood. -You need to have a master template called `contract.mustache`. it will be used to generate each contract wrapper. Although - you don't need and probably shouldn't write all your logic in a single template file. You can write [partial templates](http://handlebarsjs.com/partials.html) and as long as they are within partials folder - they will be registered and available. -## Which data/context do I get in my templates? -For now you don't get much on top of methods abi and a contract name cause if was enough for our use-case, but if you need something else - create a PR. -[Type definition](https://github.com/0xProject/0x.js/tree/development/packages/typed-contracts/src/types.ts) of what we pass to a render method. -## Output files -Output files will be generated within an output folder with names converted to camel case and taken from abi files names. If you already have some files in that folder they will be overwritten. diff --git a/packages/typed-contracts/package.json b/packages/typed-contracts/package.json deleted file mode 100644 index 6d8aa5016..000000000 --- a/packages/typed-contracts/package.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "name": "@0xproject/typed-contracts", - "version": "0.0.0", - "description": "Generate TS contract wrappers from ABI", - "main": "lib/index.js", - "types": "lib/index.d.ts", - "scripts": { - "lint": "tslint --project . 'src/**/*.ts'", - "clean": "shx rm -rf lib", - "build": "tsc" - }, - "bin": { - "typed-contracts": "lib/index.js" - }, - "repository": { - "type": "git", - "url": "https://github.com/0xProject/0x.js.git" - }, - "license": "Apache-2.0", - "bugs": { - "url": "https://github.com/0xProject/0x.js/issues" - }, - "homepage": "https://github.com/0xProject/0x.js/packages/typed-contracts/README.md", - "dependencies": { - "@types/handlebars": "^4.0.36", - "bignumber.js": "^5.0.0", - "chalk": "^2.3.0", - "glob": "^7.1.2", - "handlebars": "^4.0.11", - "lodash": "^4.17.4", - "mkdirp": "^0.5.1", - "to-snake-case": "^1.0.0", - "web3": "^0.20.0", - "yargs": "^10.0.3" - }, - "devDependencies": { - "@0xproject/tslint-config": "^0.2.0", - "@types/glob": "^5.0.33", - "@types/mkdirp": "^0.5.1", - "@types/node": "^8.0.53", - "@types/yargs": "^8.0.2", - "npm-run-all": "^4.1.1", - "shx": "^0.2.2", - "tslint": "5.8.0", - "typescript": "~2.6.1", - "web3-typescript-typings": "^0.7.2" - } -} diff --git a/packages/typed-contracts/src/globals.d.ts b/packages/typed-contracts/src/globals.d.ts deleted file mode 100644 index 39df3f852..000000000 --- a/packages/typed-contracts/src/globals.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -declare function toSnakeCase(str: string): string; -declare module 'to-snake-case' { - export = toSnakeCase; -} diff --git a/packages/typed-contracts/src/index.ts b/packages/typed-contracts/src/index.ts deleted file mode 100644 index 19d289e49..000000000 --- a/packages/typed-contracts/src/index.ts +++ /dev/null @@ -1,98 +0,0 @@ -#!/usr/bin/env node - -import chalk from 'chalk'; -import * as fs from 'fs'; -import {sync as globSync} from 'glob'; -import * as Handlebars from 'handlebars'; -import * as _ from 'lodash'; -import * as mkdirp from 'mkdirp'; -import * as yargs from 'yargs'; - -import toSnakeCase = require('to-snake-case'); -import * as Web3 from 'web3'; - -import {ContextData, ParamKind} from './types'; -import {utils} from './utils'; - -const ABI_TYPE_METHOD = 'function'; -const MAIN_TEMPLATE_NAME = 'contract.mustache'; - -const args = yargs - .option('abiGlob', { - describe: 'Glob pattern to search for ABI JSON files', - type: 'string', - demand: true, - }) - .option('templates', { - describe: 'Folder where to search for templates', - type: 'string', - demand: true, - }) - .option('output', { - describe: 'Folder where to put the output files', - type: 'string', - demand: true, - }) - .argv; - -function writeOutputFile(name: string, renderedTsCode: string): void { - const fileName = toSnakeCase(name); - const filePath = `${args.output}/${fileName}.ts`; - fs.writeFileSync(filePath, renderedTsCode); - utils.log(`Created: ${chalk.bold(filePath)}`); -} - -Handlebars.registerHelper('parameterType', utils.solTypeToTsType.bind(utils, ParamKind.Input)); -Handlebars.registerHelper('returnType', utils.solTypeToTsType.bind(utils, ParamKind.Output)); -const partialTemplateFileNames = globSync(`${args.templates}/partials/**/*.mustache`); -for (const partialTemplateFileName of partialTemplateFileNames) { - const namedContent = utils.getNamedContent(partialTemplateFileName); - Handlebars.registerPartial(namedContent.name, namedContent.content); -} - -const mainTemplate = utils.getNamedContent(`${args.templates}/${MAIN_TEMPLATE_NAME}`); -const template = Handlebars.compile(mainTemplate.content); -const abiFileNames = globSync(args.abiGlob); -if (_.isEmpty(abiFileNames)) { - utils.log(`${chalk.red(`No ABI files found.`)}`); - utils.log(`Please make sure you've passed the correct folder name and that the files have - ${chalk.bold('*.json')} extensions`); - process.exit(1); -} else { - utils.log(`Found ${chalk.green(`${abiFileNames.length}`)} ${chalk.bold('ABI')} files`); - mkdirp.sync(args.output); -} -for (const abiFileName of abiFileNames) { - const namedContent = utils.getNamedContent(abiFileName); - utils.log(`Processing: ${chalk.bold(namedContent.name)}...`); - const parsedContent = JSON.parse(namedContent.content); - const ABI = _.isArray(parsedContent) ? - parsedContent : // ABI file - parsedContent.abi; // Truffle contracts file - if (_.isUndefined(ABI)) { - utils.log(`${chalk.red(`ABI not found in ${abiFileName}.`)}`); - utils.log(`Please make sure your ABI file is either an array with ABI entries or an object with the abi key`); - process.exit(1); - } - const methodAbis = ABI.filter((abi: Web3.AbiDefinition) => abi.type === ABI_TYPE_METHOD) as Web3.MethodAbi[]; - const methodsData = _.map(methodAbis, methodAbi => { - _.map(methodAbi.inputs, input => { - if (_.isEmpty(input.name)) { - // Auto-generated getters don't have parameter names - input.name = 'index'; - } - }); - // This will make templates simpler - const methodData = { - ...methodAbi, - singleReturnValue: methodAbi.outputs.length === 1, - }; - return methodData; - }); - const contextData = { - contractName: namedContent.name, - methods: methodsData, - }; - const renderedTsCode = template(contextData); - writeOutputFile(namedContent.name, renderedTsCode); -} diff --git a/packages/typed-contracts/src/types.ts b/packages/typed-contracts/src/types.ts deleted file mode 100644 index 1dc039c83..000000000 --- a/packages/typed-contracts/src/types.ts +++ /dev/null @@ -1,15 +0,0 @@ -import * as Web3 from 'web3'; - -export enum ParamKind { - Input = 'input', - Output = 'output', -} - -export interface Method extends Web3.MethodAbi { - singleReturnValue: boolean; -} - -export interface ContextData { - contractName: string; - methods: Method[]; -} diff --git a/packages/typed-contracts/src/utils.ts b/packages/typed-contracts/src/utils.ts deleted file mode 100644 index eaf5a30cc..000000000 --- a/packages/typed-contracts/src/utils.ts +++ /dev/null @@ -1,56 +0,0 @@ -import * as fs from 'fs'; -import * as _ from 'lodash'; -import * as path from 'path'; - -import {ParamKind} from './types'; - -export const utils = { - solTypeToTsType(paramKind: ParamKind, solType: string): string { - const trailingArrayRegex = /\[\d*\]$/; - if (solType.match(trailingArrayRegex)) { - const arrayItemSolType = solType.replace(trailingArrayRegex, ''); - const arrayItemTsType = utils.solTypeToTsType(paramKind, arrayItemSolType); - const arrayTsType = `${arrayItemTsType}[]`; - return arrayTsType; - } else { - const solTypeRegexToTsType = [ - {regex: '^string$', tsType: 'string'}, - {regex: '^address$', tsType: 'string'}, - {regex: '^bool$', tsType: 'boolean'}, - {regex: '^u?int\\d*$', tsType: 'BigNumber'}, - {regex: '^bytes\\d*$', tsType: 'string'}, - ]; - if (paramKind === ParamKind.Input) { - // web3 allows to pass those an non-bignumbers and that's nice - // but it always returns stuff as BigNumbers - solTypeRegexToTsType.unshift({regex: '^u?int(8|16|32)?$', tsType: 'number|BigNumber'}); - } - for (const regexAndTxType of solTypeRegexToTsType) { - const {regex, tsType} = regexAndTxType; - if (solType.match(regex)) { - return tsType; - } - } - throw new Error(`Unknown Solidity type found: ${solType}`); - } - }, - log(...args: any[]): void { - console.log(...args); // tslint:disable-line:no-console - }, - getPartialNameFromFileName(filename: string): string { - const name = path.parse(filename).name; - return name; - }, - getNamedContent(filename: string): {name: string; content: string} { - const name = utils.getPartialNameFromFileName(filename); - try { - const content = fs.readFileSync(filename).toString(); - return { - name, - content, - }; - } catch (err) { - throw new Error(`Failed to read ${filename}: ${err}`); - } - }, -}; diff --git a/packages/typed-contracts/tsconfig.json b/packages/typed-contracts/tsconfig.json deleted file mode 100644 index 2a3667890..000000000 --- a/packages/typed-contracts/tsconfig.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "target": "es5", - "lib": ["es2015", "dom"], - "outDir": "lib", - "sourceMap": true, - "declaration": true, - "noImplicitAny": true, - "strictNullChecks": true - }, - "include": [ - "./src/**/*", - "./test/**/*", - "../../node_modules/web3-typescript-typings/index.d.ts", - ] -} diff --git a/packages/typed-contracts/tslint.json b/packages/typed-contracts/tslint.json deleted file mode 100644 index a07795151..000000000 --- a/packages/typed-contracts/tslint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": [ - "@0xproject/tslint-config" - ] -} -- cgit v1.2.3