Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add truffle v5 target, switch from BigNumber to BN #196

Merged
merged 12 commits into from
Dec 27, 2019
Merged
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
21 changes: 21 additions & 0 deletions packages/typechain-target-truffle-v5/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Typechain target Truffle
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can u add here Truffle@5 or something?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


<p align="center">
<img src="https://github.com/Neufund/TypeChain/blob/d82f3cc644a11e22ca8e42505c16f035e2f2555d/docs/images/typechain-logo.png?raw=true" width="300" alt="TypeChain">
<h3 align="center">TypeChain target Truffle v5</h3>
<p align="center">🔌 TypeScript bindings for Truffle v5 smartcontracts</p>

<p align="center">
<a href="https://circleci.com/gh/ethereum-ts/TypeChain"><img alt="Build Status" src="https://circleci.com/gh/ethereum-ts/TypeChain/tree/master.svg?style=svg"></a>
<a href="https://coveralls.io/github/ethereum-ts/TypeChain?branch=master"><img alt="Coverage" src="https://coveralls.io/repos/github/ethereum-ts/TypeChain/badge.svg?branch=master"></a>
<img alt="Downloads" src="https://img.shields.io/npm/dm/typechain.svg">
<a href="https://github.com/prettier/prettier"><img alt="Prettier" src="https://img.shields.io/badge/code_style-prettier-ff69b4.svg"></a>
<a href="/package.json"><img alt="Software License" src="https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square"></a>
</p>

<p align="center">
<a href="https://blog.neufund.org/introducing-typechain-typescript-bindings-for-ethereum-smart-contracts-839fc2becf22">Medium post</a> | <a href="https://www.youtube.com/watch?v=9x6AkShGkwU">DappCon Video</a>
</p>
</p>

## [TypeChain readme](https://github.com/ethereum-ts/TypeChain)
175 changes: 175 additions & 0 deletions packages/typechain-target-truffle-v5/lib/generation.ts
Original file line number Diff line number Diff line change
@@ -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 = `
/// <reference types="truffle-typings" />
import BN from "bn.js";

${contracts.map(generateContractInterface).join("\n")}

${contracts.map(generateContractInstanceInterface).join("\n")}
`;

return template;
}

export function generateArtifactHeaders(contracts: Contract[]): string {
return `
/// <reference types="truffle-typings" />

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<Truffle.TransactionResponse>;
call(${generateInputTypes(
fn.inputs,
)} txDetails?: Truffle.TransactionDetails): Promise<${generateOutputTypes(fn.outputs)}>;
sendTransaction(${generateInputTypes(
fn.inputs,
)} txDetails?: Truffle.TransactionDetails): Promise<string>;
estimateGas(${generateInputTypes(
fn.inputs,
)} txDetails?: Truffle.TransactionDetails): Promise<number>;
}
`;
}

function generateConstantFunction(fn: FunctionDeclaration): string {
return `
${fn.name}(${generateInputTypes(
fn.inputs,
)} txDetails?: Truffle.TransactionDetails): Promise<${generateOutputTypes(fn.outputs)}>;
`;
}

function generateInputTypes(input: Array<AbiParameter>): string {
if (input.length === 0) {
return "";
}
return (
input
.map((input, index) => `${input.name || `arg${index}`}: ${generateInputType(input.type)}`)
.join(", ") + ", "
);
}

function generateOutputTypes(outputs: Array<AbiOutputParameter>): 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(", ") +
"}"
);
}
53 changes: 53 additions & 0 deletions packages/typechain-target-truffle-v5/lib/index.ts
Original file line number Diff line number Diff line change
@@ -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<ITruffleCfg>) {
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),
},
];
}
}
29 changes: 29 additions & 0 deletions packages/typechain-target-truffle-v5/package.json
Original file line number Diff line number Diff line change
@@ -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"
}
2 changes: 1 addition & 1 deletion packages/typechain-target-truffle/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "typechain-target-truffle",
"description": "TypeChain target with truffle support",
"description": "TypeChain target with truffle v4 support",
"keywords": [
"ethereum",
"typescript",
Expand Down