diff --git a/package.json b/package.json index 387968829..6766f7262 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "license": "MIT", "scripts": { "prepare": "patch-package", - "build": "rm -rf ./dist && tsc -p ./tsconfig.prod.json && (cd packages/core/ && yarn build) && (cd packages/typechain-target-ethers/ && yarn build) && (cd packages/typechain-target-truffle/ && yarn build) && (cd packages/typechain-target-web3-v1/ && yarn build) && (cd packages/typechain-target-web3-v2/ && yarn build)", + "build": "rm -rf ./dist && tsc -p ./tsconfig.prod.json && (cd packages/core/ && yarn build) && (cd packages/typechain-target-ethers/ && yarn build) && (cd packages/typechain-target-truffle/ && yarn build) && (cd packages/typechain-target-truffle-v5/ && yarn build) && (cd packages/typechain-target-web3-v1/ && yarn build) && (cd packages/typechain-target-web3-v2/ && yarn build)", "tslint": "tslint -p ./tsconfig.json -e 'node_modules/**/*' -e '**/node_modules/**/*' 'packages/**/*.ts'", "tslint:fix": "tslint --fix --format stylish -p ./tsconfig.json -e 'node_modules/**/*' -e '**/node_modules/**/*' 'packages/**/*.ts'", "format": "prettier --list-different 'packages/**/*.{ts,tsx,json,md,gql}' README.md", diff --git a/packages/typechain-target-truffle-v5/README.md b/packages/typechain-target-truffle-v5/README.md new file mode 100644 index 000000000..bd9444dbc --- /dev/null +++ b/packages/typechain-target-truffle-v5/README.md @@ -0,0 +1,21 @@ +# Typechain target Truffle + +

+ TypeChain +

TypeChain target Truffle v5

+

🔌 TypeScript bindings for Truffle v5 smartcontracts

+ +

+ Build Status + Coverage + Downloads + Prettier + Software License +

+ +

+ Medium post | DappCon Video +

+

+ +## [TypeChain readme](https://github.com/ethereum-ts/TypeChain) diff --git a/packages/typechain-target-truffle-v5/lib/generation.ts b/packages/typechain-target-truffle-v5/lib/generation.ts new file mode 100644 index 000000000..f7f55d53a --- /dev/null +++ b/packages/typechain-target-truffle-v5/lib/generation.ts @@ -0,0 +1,175 @@ +import { + Contract, + AbiParameter, + FunctionDeclaration, + isConstant, + isConstantFn, + AbiOutputParameter, + EvmType, + TupleType, + EvmOutputType, +} from "typechain"; +import { values } from "lodash"; + +export function codegen(contracts: Contract[]) { + const template = ` +/// +import BN from "bn.js"; + +${contracts.map(generateContractInterface).join("\n")} + +${contracts.map(generateContractInstanceInterface).join("\n")} + `; + + return template; +} + +export function generateArtifactHeaders(contracts: Contract[]): string { + return ` + /// + + import * as TruffleContracts from "."; + + declare global { + namespace Truffle { + interface Artifacts { + ${contracts + .map(f => `require(name: "${f.name}"): TruffleContracts.${f.name}Contract;`) + .join("\n")} + } + } + } + `; +} + +function generateContractInterface(c: Contract): string { + return ` +export interface ${c.name}Contract extends Truffle.Contract<${c.name}Instance> { + ${ + c.constructor && c.constructor[0] + ? `"new"(${generateInputTypes( + c.constructor[0].inputs, + )} meta?: Truffle.TransactionDetails): Promise<${c.name}Instance>;` + : `"new"(meta?: Truffle.TransactionDetails): Promise<${c.name}Instance>;` + } +} +`; +} + +function generateContractInstanceInterface(c: Contract): string { + return ` +export interface ${c.name}Instance extends Truffle.ContractInstance { + ${values(c.functions) + .map(v => v[0]) + .map(generateFunction) + .join("\n")} +} + `; +} + +function generateFunction(fn: FunctionDeclaration): string { + if (isConstant(fn) || isConstantFn(fn)) { + return generateConstantFunction(fn); + } + + return ` + ${fn.name}: { + (${generateInputTypes( + fn.inputs, + )} txDetails?: Truffle.TransactionDetails): Promise; + call(${generateInputTypes( + fn.inputs, + )} txDetails?: Truffle.TransactionDetails): Promise<${generateOutputTypes(fn.outputs)}>; + sendTransaction(${generateInputTypes( + fn.inputs, + )} txDetails?: Truffle.TransactionDetails): Promise; + estimateGas(${generateInputTypes( + fn.inputs, + )} txDetails?: Truffle.TransactionDetails): Promise; + } +`; +} + +function generateConstantFunction(fn: FunctionDeclaration): string { + return ` + ${fn.name}(${generateInputTypes( + fn.inputs, + )} txDetails?: Truffle.TransactionDetails): Promise<${generateOutputTypes(fn.outputs)}>; +`; +} + +function generateInputTypes(input: Array): string { + if (input.length === 0) { + return ""; + } + return ( + input + .map((input, index) => `${input.name || `arg${index}`}: ${generateInputType(input.type)}`) + .join(", ") + ", " + ); +} + +function generateOutputTypes(outputs: Array): string { + if (outputs.length === 1) { + return generateOutputType(outputs[0].type); + } else { + return `[${outputs.map(param => generateOutputType(param.type)).join(", ")}]`; + } +} + +function generateInputType(evmType: EvmType): string { + switch (evmType.type) { + case "integer": + return "number | BN | string"; + case "uinteger": + return "number | BN | string"; + case "address": + return "string | BN"; + case "bytes": + return "string | BN"; + case "dynamic-bytes": + return "string"; + case "array": + return `(${generateInputType(evmType.itemType)})[]`; + case "boolean": + return "boolean"; + case "string": + return "string"; + case "tuple": + return generateTupleType(evmType, generateInputType); + } +} + +function generateOutputType(evmType: EvmOutputType): string { + switch (evmType.type) { + case "integer": + return "BN"; + case "uinteger": + return "BN"; + case "address": + return "string"; + case "void": + return "void"; + case "bytes": + case "dynamic-bytes": + return "string"; + case "array": + return `(${generateOutputType(evmType.itemType)})[]`; + case "boolean": + return "boolean"; + case "string": + return "string"; + case "tuple": + return generateTupleType(evmType, generateOutputType); + } +} + +function generateTupleType(tuple: TupleType, generator: (evmType: EvmType) => string) { + return ( + "{" + + tuple.components + .map(component => `${component.name}: ${generator(component.type)}`) + .join(", ") + + "}" + ); +} diff --git a/packages/typechain-target-truffle-v5/lib/index.ts b/packages/typechain-target-truffle-v5/lib/index.ts new file mode 100644 index 000000000..b5ef4bbf7 --- /dev/null +++ b/packages/typechain-target-truffle-v5/lib/index.ts @@ -0,0 +1,53 @@ +import { Contract, getFilename, extractAbi, parse } from "typechain"; +import { TsGeneratorPlugin, TContext, TFileDesc } from "ts-generator"; +import { join, resolve } from "path"; + +import { codegen, generateArtifactHeaders } from "./generation"; + +export interface ITruffleCfg { + outDir?: string; +} + +const DEFAULT_OUT_PATH = "./types/truffle-contracts/"; + +export default class Truffle extends TsGeneratorPlugin { + name = "Truffle"; + + private readonly outDirAbs: string; + private contracts: Contract[] = []; + + constructor(ctx: TContext) { + super(ctx); + + const { cwd, rawConfig } = ctx; + + this.outDirAbs = resolve(cwd, rawConfig.outDir || DEFAULT_OUT_PATH); + } + + transformFile(file: TFileDesc): TFileDesc | void { + const abi = extractAbi(file.contents); + const isEmptyAbi = abi.length === 0; + if (isEmptyAbi) { + return; + } + + const name = getFilename(file.path); + + const contract = parse(abi, name); + + this.contracts.push(contract); + } + + afterRun(): TFileDesc[] { + return [ + { + path: join(this.outDirAbs, "index.d.ts"), + contents: codegen(this.contracts), + }, + { + path: join(this.outDirAbs, "merge.d.ts"), + contents: generateArtifactHeaders(this.contracts), + }, + ]; + } +} diff --git a/packages/typechain-target-truffle-v5/package.json b/packages/typechain-target-truffle-v5/package.json new file mode 100644 index 000000000..927064ced --- /dev/null +++ b/packages/typechain-target-truffle-v5/package.json @@ -0,0 +1,29 @@ +{ + "name": "typechain-target-truffle-v5", + "description": "TypeChain target with truffle v5 support", + "keywords": [ + "ethereum", + "typescript", + "bindings", + "truffle" + ], + "version": "1.0.2", + "license": "MIT", + "repository": "https://github.com/ethereum-ts/Typechain", + "devDependencies": { + "truffle": "^5.1.1", + "truffle-typings": "^1.0.8" + }, + "dependencies": { + "lodash": "^4.17.15", + "@types/bn.js": "4.11.5" + }, + "peerDependencies": { + "typechain": "^1.0.0" + }, + "scripts": { + "build": "rm -rf ./dist && cp -R '../../dist/typechain-target-truffle-v5/lib' ./dist/", + "prepublishOnly": "cd .. && yarn prepublishOnly" + }, + "main": "./dist/index.js" +} diff --git a/packages/typechain-target-truffle/package.json b/packages/typechain-target-truffle/package.json index cfde9c625..45e98686a 100644 --- a/packages/typechain-target-truffle/package.json +++ b/packages/typechain-target-truffle/package.json @@ -1,6 +1,6 @@ { "name": "typechain-target-truffle", - "description": "TypeChain target with truffle support", + "description": "TypeChain target with truffle v4 support", "keywords": [ "ethereum", "typescript",