Merge pull request #1004 from feuGeneA/sol-doc

[ethereum-types, react-docs, sol-compiler, sol-doc, types, typescript-typings, website] Add new Solidity documentation generation utility
This commit is contained in:
F. Eugene Aumson
2018-09-26 10:15:53 -04:00
committed by GitHub
46 changed files with 1405 additions and 610 deletions

View File

@@ -100,6 +100,7 @@ jobs:
- run: yarn wsrun test:circleci @0xproject/order-watcher
- run: yarn wsrun test:circleci @0xproject/sol-compiler
- run: yarn wsrun test:circleci @0xproject/sol-cov
- run: yarn wsrun test:circleci @0xproject/sol-doc
- run: yarn wsrun test:circleci @0xproject/sra-report
- run: yarn wsrun test:circleci @0xproject/subproviders
- run: yarn wsrun test:circleci @0xproject/web3-wrapper
@@ -152,6 +153,10 @@ jobs:
key: coverage-sol-cov-{{ .Environment.CIRCLE_SHA1 }}
paths:
- ~/repo/packages/sol-cov/coverage/lcov.info
- save_cache:
key: coverage-sol-doc-{{ .Environment.CIRCLE_SHA1 }}
paths:
- ~/repo/packages/sol-doc/coverage/lcov.info
- save_cache:
key: coverage-sra-report-{{ .Environment.CIRCLE_SHA1 }}
paths:
@@ -218,6 +223,9 @@ jobs:
- restore_cache:
keys:
- coverage-sol-cov-{{ .Environment.CIRCLE_SHA1 }}
- restore_cache:
keys:
- coverage-sol-doc-{{ .Environment.CIRCLE_SHA1 }}
- restore_cache:
keys:
- coverage-sra-report-{{ .Environment.CIRCLE_SHA1 }}

View File

@@ -36,7 +36,8 @@
"generate_doc": "node ./packages/monorepo-scripts/lib/doc_generate_and_upload.js",
"test:generate_docs:circleci": "for i in ${npm_package_config_packagesWithDocPages}; do yarn generate_doc --package $i --shouldUpload false --isStaging true || break -1; done;",
"lint": "wsrun lint $PKG --fast-exit --parallel --exclude-missing",
"comment:postinstall": "HACK: For some reason `yarn` is not setting up symlinks properly for order-utils. We temporarily set them manually. Remove this after V2 refactor is complete."
"comment:postinstall": "HACK: For some reason `yarn` is not setting up symlink properly for types. We temporarily make it manually. Remove this ASAP!",
"postinstall": "shx cp -R node_modules/@0xproject/types packages/website/node_modules/@0xproject; shx cp -R node_modules/@0xproject/types packages/sra-report/node_modules/@0xproject; rimraf node_modules/@0xproject/types; shx ln -s ../../packages/types node_modules/@0xproject/types"
},
"config": {
"mnemonic": "concert load couple harbor equip island argue ramp clarify fence smart topic",

View File

@@ -325,9 +325,57 @@ export interface ContractNetworkData {
constructorArgs: string;
}
export type ParamDescription = string;
export interface StandardContractOutput {
abi: ContractAbi;
evm: EvmOutput;
devdoc?: DevdocOutput;
}
export interface StandardOutput {
errors: SolcError[];
sources: {
[fileName: string]: {
id: number;
ast?: object;
legacyAST?: object;
};
};
contracts: {
[fileName: string]: {
[contractName: string]: StandardContractOutput;
};
};
}
export type ErrorType =
| 'JSONError'
| 'IOError'
| 'ParserError'
| 'DocstringParsingError'
| 'SyntaxError'
| 'DeclarationError'
| 'TypeError'
| 'UnimplementedFeatureError'
| 'InternalCompilerError'
| 'Exception'
| 'CompilerError'
| 'FatalError'
| 'Warning';
export type ErrorSeverity = 'error' | 'warning';
export interface SolcError {
sourceLocation?: {
file: string;
start: number;
end: number;
};
type: ErrorType;
component: 'general' | 'ewasm';
severity: ErrorSeverity;
message: string;
formattedMessage?: string;
}
export interface EvmOutput {
@@ -340,6 +388,20 @@ export interface EvmBytecodeOutput {
sourceMap: string;
}
export interface DevdocOutput {
title: string;
author: string;
methods: {
[signature: string]: {
details: string;
params: {
[name: string]: ParamDescription;
};
return?: string;
};
};
}
export interface ContractVersionData {
compiler: CompilerOpts;
sources: {

View File

@@ -2,7 +2,7 @@
#### WARNING: Alpha software. Expect things to break when trying to use.
A full-page React component for rendering beautiful documentation for Solidity and Typescript code generated with [TypeDoc](http://typedoc.org/) or [Doxity](https://github.com/0xproject/doxity).
A full-page React component for rendering beautiful documentation for Solidity and Typescript code generated with [TypeDoc](http://typedoc.org/) or [sol-doc](https://github.com/0xProject/0x-monorepo/tree/development/packages/sol-doc).
<div style="text-align: center;">
<img src="https://s3.eu-west-2.amazonaws.com/0x-wiki-images/screenshot.png" style="padding-bottom: 20px; padding-top: 20px;" width="80%" />
@@ -47,7 +47,7 @@ Feel free to contribute to these improvements!
* Allow user to pass in styling for all major elements similar to [Material-UI](http://www.material-ui.com/).
* Allow user to define an alternative font and have it change everywhere.
* Add source links to Solidity docs (currently unsupported by Doxity).
* Add source links to Solidity docs (currently unsupported by solc, which underlies sol-doc).
## Contributing

View File

@@ -4,7 +4,7 @@
"engines": {
"node": ">=6.12"
},
"description": "React documentation component for rendering TypeDoc & Doxity generated JSON",
"description": "React documentation component for rendering TypeDoc & sol-doc generated JSON",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"scripts": {
@@ -34,6 +34,7 @@
},
"dependencies": {
"@0xproject/react-shared": "^1.0.12",
"@0xproject/types": "^1.1.0",
"@0xproject/utils": "^1.0.11",
"@types/lodash": "4.14.104",
"@types/material-ui": "^0.20.0",

View File

@@ -2,7 +2,7 @@ import { logUtils } from '@0xproject/utils';
import * as _ from 'lodash';
import * as React from 'react';
import { CustomType } from '../types';
import { CustomType } from '@0xproject/types';
const STRING_ENUM_CODE_PREFIX = ' strEnum(';

View File

@@ -9,24 +9,23 @@ import {
Styles,
utils as sharedUtils,
} from '@0xproject/react-shared';
import {
DocAgnosticFormat,
Event,
ExternalExportToLink,
Property,
SolidityMethod,
TypeDefinitionByName,
TypescriptFunction,
TypescriptMethod,
} from '@0xproject/types';
import * as _ from 'lodash';
import CircularProgress from 'material-ui/CircularProgress';
import * as React from 'react';
import * as semver from 'semver';
import { DocsInfo } from '../docs_info';
import {
AddressByContractName,
DocAgnosticFormat,
Event,
ExternalExportToLink,
Property,
SolidityMethod,
SupportedDocJson,
TypeDefinitionByName,
TypescriptFunction,
TypescriptMethod,
} from '../types';
import { AddressByContractName, SupportedDocJson } from '../types';
import { constants } from '../utils/constants';
import { Badge } from './badge';
@@ -330,7 +329,7 @@ export class Documentation extends React.Component<DocumentationProps, Documenta
return <div>{externalExports}</div>;
}
private _renderNetworkBadgesIfExists(sectionName: string): React.ReactNode {
if (this.props.docsInfo.type !== SupportedDocJson.Doxity) {
if (this.props.docsInfo.type !== SupportedDocJson.SolDoc) {
return null;
}

View File

@@ -1,9 +1,9 @@
import { AnchorTitle, colors, HeaderSizes } from '@0xproject/react-shared';
import { Event, EventArg } from '@0xproject/types';
import * as _ from 'lodash';
import * as React from 'react';
import { DocsInfo } from '../docs_info';
import { Event, EventArg } from '../types';
import { Type } from './type';

View File

@@ -1,8 +1,9 @@
import * as _ from 'lodash';
import * as React from 'react';
import { CustomType, TypeDefinitionByName } from '@0xproject/types';
import { DocsInfo } from '../docs_info';
import { CustomType, TypeDefinitionByName } from '../types';
import { Signature } from './signature';
import { Type } from './type';

View File

@@ -1,8 +1,8 @@
import { AnchorTitle, HeaderSizes } from '@0xproject/react-shared';
import { Property, TypeDefinitionByName } from '@0xproject/types';
import * as React from 'react';
import { DocsInfo } from '../docs_info';
import { Property, TypeDefinitionByName } from '../types';
import { constants } from '../utils/constants';
import { Comment } from './comment';

View File

@@ -1,8 +1,9 @@
import * as _ from 'lodash';
import * as React from 'react';
import { Parameter, Type as TypeDef, TypeDefinitionByName, TypeParameter } from '@0xproject/types';
import { DocsInfo } from '../docs_info';
import { Parameter, Type as TypeDef, TypeDefinitionByName, TypeParameter } from '../types';
import { Type } from './type';

View File

@@ -1,9 +1,15 @@
import { AnchorTitle, colors, HeaderSizes, Styles } from '@0xproject/react-shared';
import {
Parameter,
SolidityMethod,
TypeDefinitionByName,
TypescriptFunction,
TypescriptMethod,
} from '@0xproject/types';
import * as _ from 'lodash';
import * as React from 'react';
import { DocsInfo } from '../docs_info';
import { Parameter, SolidityMethod, TypeDefinitionByName, TypescriptFunction, TypescriptMethod } from '../types';
import { constants } from '../utils/constants';
import { Comment } from './comment';

View File

@@ -1,8 +1,7 @@
import { colors } from '@0xproject/react-shared';
import { Source } from '@0xproject/types';
import * as React from 'react';
import { Source } from '../types';
export interface SourceLinkProps {
source: Source;
sourceUrl: string;

View File

@@ -1,4 +1,5 @@
import { colors, constants as sharedConstants, utils as sharedUtils } from '@0xproject/react-shared';
import { Type as TypeDef, TypeDefinitionByName, TypeDocTypes } from '@0xproject/types';
import { errorUtils } from '@0xproject/utils';
import * as _ from 'lodash';
import * as React from 'react';
@@ -6,7 +7,6 @@ import { Link as ScrollLink } from 'react-scroll';
import * as ReactTooltip from 'react-tooltip';
import { DocsInfo } from '../docs_info';
import { Type as TypeDef, TypeDefinitionByName, TypeDocTypes } from '../types';
import { constants } from '../utils/constants';
import { Signature } from './signature';

View File

@@ -1,10 +1,11 @@
import { AnchorTitle, colors, HeaderSizes } from '@0xproject/react-shared';
import { CustomType, CustomTypeChild, TypeDefinitionByName, TypeDocTypes } from '@0xproject/types';
import { errorUtils } from '@0xproject/utils';
import * as _ from 'lodash';
import * as React from 'react';
import { DocsInfo } from '../docs_info';
import { CustomType, CustomTypeChild, KindString, TypeDefinitionByName, TypeDocTypes } from '../types';
import { KindString } from '../types';
import { constants } from '../utils/constants';
import { Comment } from './comment';

View File

@@ -1,20 +1,15 @@
import { MenuSubsectionsBySection } from '@0xproject/react-shared';
import { DocAgnosticFormat, TypeDefinitionByName } from '@0xproject/types';
import * as _ from 'lodash';
import {
ContractsByVersionByNetworkId,
DocAgnosticFormat,
DocsInfoConfig,
DocsMenu,
DoxityDocObj,
GeneratedDocJson,
SectionNameToMarkdownByVersion,
SectionsMap,
SupportedDocJson,
TypeDefinitionByName,
} from './types';
import { doxityUtils } from './utils/doxity_utils';
import { TypeDocUtils } from './utils/typedoc_utils';
export class DocsInfo {
public id: string;
@@ -95,12 +90,4 @@ export class DocsInfo {
const typeDefinitionByName = _.keyBy(typeDocSection.types, 'name') as any;
return typeDefinitionByName;
}
public convertToDocAgnosticFormat(docObj: DoxityDocObj | GeneratedDocJson): DocAgnosticFormat {
if (this.type === SupportedDocJson.Doxity) {
return doxityUtils.convertToDocAgnosticFormat(docObj as DoxityDocObj);
} else {
const typeDocUtils = new TypeDocUtils(docObj as GeneratedDocJson, this);
return typeDocUtils.convertToDocAgnosticFormat();
}
}
}

View File

@@ -1,3 +1,5 @@
export { DocAgnosticFormat, GeneratedDocJson } from '@0xproject/types';
// Exported to give users of this library added flexibility if they want to build
// a docs page from scratch using the individual components.
export { Badge } from './components/badge';
@@ -12,17 +14,10 @@ export { Signature } from './components/signature';
export { SourceLink } from './components/source_link';
export { TypeDefinition } from './components/type_definition';
export { Type } from './components/type';
export { TypeDocUtils } from './utils/typedoc_utils';
export { DocsInfo } from './docs_info';
export {
DocsInfoConfig,
DocAgnosticFormat,
DoxityDocObj,
DocsMenu,
SupportedDocJson,
TypeDocNode,
GeneratedDocJson,
} from './types';
export { DocsInfoConfig, DocsMenu, SupportedDocJson } from './types';
export { constants } from './utils/constants';

View File

@@ -22,72 +22,6 @@ export interface SectionsMap {
[sectionName: string]: string;
}
export interface TypeDocType {
type: TypeDocTypes;
value: string;
name: string;
types: TypeDocType[];
typeArguments?: TypeDocType[];
declaration: TypeDocNode;
elementType?: TypeDocType;
indexSignature?: TypeDocNode;
elements?: TupleElement[];
}
export interface TupleElement {
type: string;
name: string;
}
export interface TypeDocFlags {
isStatic?: boolean;
isOptional?: boolean;
isPublic?: boolean;
isExported?: boolean;
}
export interface TypeDocGroup {
title: string;
children: number[];
}
export interface TypeDocNode {
id?: number;
name?: string;
kind?: string;
defaultValue?: string;
kindString?: string;
type?: TypeDocType;
fileName?: string;
line?: number;
comment?: TypeDocNode;
text?: string;
shortText?: string;
returns?: string;
declaration: TypeDocNode;
flags?: TypeDocFlags;
indexSignature?: TypeDocNode;
signatures?: TypeDocNode[];
parameters?: TypeDocNode[];
typeParameter?: TypeDocNode[];
sources?: TypeDocNode[];
children?: TypeDocNode[];
groups?: TypeDocGroup[];
}
export enum TypeDocTypes {
Intrinsic = 'intrinsic',
Reference = 'reference',
Array = 'array',
StringLiteral = 'stringLiteral',
Reflection = 'reflection',
Union = 'union',
TypeParameter = 'typeParameter',
Intersection = 'intersection',
Tuple = 'tuple',
Unknown = 'unknown',
}
// Exception: We don't make the values uppercase because these KindString's need to
// match up those returned by TypeDoc
export enum KindString {
@@ -103,141 +37,8 @@ export enum KindString {
Class = 'Class',
}
export interface DocAgnosticFormat {
[sectionName: string]: DocSection;
}
export interface DocSection {
comment: string;
constructors: Array<TypescriptMethod | SolidityMethod>;
methods: Array<TypescriptMethod | SolidityMethod>;
properties: Property[];
types: CustomType[];
functions: TypescriptFunction[];
events?: Event[];
externalExportToLink?: ExternalExportToLink;
}
export interface TypescriptMethod extends BaseMethod {
source?: Source;
isStatic?: boolean;
typeParameter?: TypeParameter;
}
export interface TypescriptFunction extends BaseFunction {
source?: Source;
typeParameter?: TypeParameter;
callPath: string;
}
export interface SolidityMethod extends BaseMethod {
isConstant?: boolean;
isPayable?: boolean;
}
export interface Source {
fileName: string;
line: number;
}
export interface Parameter {
name: string;
comment: string;
isOptional: boolean;
type: Type;
defaultValue?: string;
}
export interface TypeParameter {
name: string;
type?: Type;
}
export interface Type {
name: string;
typeDocType: TypeDocTypes;
value?: string;
isExportedClassReference?: boolean;
typeArguments?: Type[];
elementType?: ElementType;
types?: Type[];
method?: TypescriptMethod;
indexSignature?: IndexSignature;
externalLink?: string;
tupleElements?: Type[];
}
export interface ElementType {
name: string;
typeDocType: TypeDocTypes;
}
export interface IndexSignature {
keyName: string;
keyType: Type;
valueName: string;
}
export interface CustomType {
name: string;
kindString: string;
type?: Type;
method?: TypescriptMethod;
indexSignature?: IndexSignature;
defaultValue?: string;
comment?: string;
children?: CustomTypeChild[];
}
export interface CustomTypeChild {
name: string;
type?: Type;
defaultValue?: string;
}
export interface Event {
name: string;
eventArgs: EventArg[];
}
export interface EventArg {
isIndexed: boolean;
name: string;
type: Type;
}
export interface Property {
name: string;
type: Type;
source?: Source;
comment?: string;
callPath?: string;
}
export interface BaseMethod {
isConstructor: boolean;
name: string;
returnComment?: string | undefined;
callPath: string;
parameters: Parameter[];
returnType: Type;
comment?: string;
}
export interface BaseFunction {
name: string;
returnComment?: string | undefined;
parameters: Parameter[];
returnType: Type;
comment?: string;
}
export interface TypeDefinitionByName {
[typeName: string]: CustomType;
}
export enum SupportedDocJson {
Doxity = 'DOXITY',
SolDoc = 'SOL_DOC',
TypeDoc = 'TYPEDOC',
}
@@ -249,40 +50,6 @@ export interface ContractsByVersionByNetworkId {
};
}
export interface DoxityDocObj {
[contractName: string]: DoxityContractObj;
}
export interface DoxityContractObj {
title: string;
fileName: string;
name: string;
abiDocs: DoxityAbiDoc[];
}
export interface DoxityAbiDoc {
constant: boolean;
inputs: DoxityInput[];
name: string;
outputs: DoxityOutput[];
payable: boolean;
type: string;
details?: string;
return?: string;
}
export interface DoxityOutput {
name: string;
type: string;
}
export interface DoxityInput {
name: string;
type: string;
description: string;
indexed?: boolean;
}
export interface AddressByContractName {
[contractName: string]: string;
}
@@ -297,28 +64,3 @@ export enum AbiTypes {
Function = 'function',
Event = 'event',
}
export interface ExportNameToTypedocNames {
[exportName: string]: string[];
}
export interface ExternalTypeToLink {
[externalTypeName: string]: string;
}
export interface ExternalExportToLink {
[externalExport: string]: string;
}
export interface Metadata {
exportPathToTypedocNames: ExportNameToTypedocNames;
exportPathOrder: string[];
externalTypeToLink: ExternalTypeToLink;
externalExportToLink: ExternalExportToLink;
}
export interface GeneratedDocJson {
version: string;
metadata: Metadata;
typedocJson: TypeDocNode;
}

View File

@@ -4,7 +4,7 @@ export const constants = {
TYPES_SECTION_NAME: 'types',
EXTERNAL_EXPORTS_SECTION_NAME: 'external exports',
TYPE_TO_SYNTAX: {
[SupportedDocJson.Doxity]: 'solidity',
[SupportedDocJson.SolDoc]: 'solidity',
[SupportedDocJson.TypeDoc]: 'typescript',
} as { [supportedDocType: string]: string },
};

View File

@@ -1,176 +0,0 @@
import * as _ from 'lodash';
import {
AbiTypes,
DocAgnosticFormat,
DocSection,
DoxityAbiDoc,
DoxityContractObj,
DoxityDocObj,
DoxityInput,
EventArg,
Parameter,
Property,
SolidityMethod,
Type,
TypeDocTypes,
} from '../types';
export const doxityUtils = {
convertToDocAgnosticFormat(doxityDocObj: DoxityDocObj): DocAgnosticFormat {
const docAgnosticFormat: DocAgnosticFormat = {};
_.each(doxityDocObj, (doxityContractObj: DoxityContractObj, contractName: string) => {
const doxityConstructor = _.find(doxityContractObj.abiDocs, (abiDoc: DoxityAbiDoc) => {
return abiDoc.type === AbiTypes.Constructor;
});
const constructors = [];
if (!_.isUndefined(doxityConstructor)) {
const constructor = {
isConstructor: true,
name: doxityContractObj.name,
comment: doxityConstructor.details,
returnComment: doxityConstructor.return,
callPath: '',
parameters: doxityUtils._convertParameters(doxityConstructor.inputs),
returnType: doxityUtils._convertType(doxityContractObj.name),
};
constructors.push(constructor);
}
const doxityMethods: DoxityAbiDoc[] = _.filter<DoxityAbiDoc>(
doxityContractObj.abiDocs,
(abiDoc: DoxityAbiDoc) => {
return doxityUtils._isMethod(abiDoc);
},
);
const methods: SolidityMethod[] = _.map<DoxityAbiDoc, SolidityMethod>(
doxityMethods,
(doxityMethod: DoxityAbiDoc) => {
const outputs = !_.isUndefined(doxityMethod.outputs) ? doxityMethod.outputs : [];
let returnTypeIfExists: Type;
if (outputs.length === 0) {
// no-op. It's already undefined
} else if (outputs.length === 1) {
const outputsType = outputs[0].type;
returnTypeIfExists = doxityUtils._convertType(outputsType);
} else {
const outputsType = `[${_.map(outputs, output => output.type).join(', ')}]`;
returnTypeIfExists = doxityUtils._convertType(outputsType);
}
// For ZRXToken, we want to convert it to zrxToken, rather then simply zRXToken
const callPath =
contractName !== 'ZRXToken'
? `${contractName[0].toLowerCase()}${contractName.slice(1)}.`
: `${contractName.slice(0, 3).toLowerCase()}${contractName.slice(3)}.`;
const method = {
isConstructor: false,
isConstant: doxityMethod.constant,
isPayable: doxityMethod.payable,
name: doxityMethod.name,
comment: doxityMethod.details,
returnComment: doxityMethod.return,
callPath,
parameters: doxityUtils._convertParameters(doxityMethod.inputs),
returnType: returnTypeIfExists,
};
return method;
},
);
const doxityProperties: DoxityAbiDoc[] = _.filter<DoxityAbiDoc>(
doxityContractObj.abiDocs,
(abiDoc: DoxityAbiDoc) => {
return doxityUtils._isProperty(abiDoc);
},
);
const properties = _.map<DoxityAbiDoc, Property>(doxityProperties, (doxityProperty: DoxityAbiDoc) => {
// We assume that none of our functions return more then a single return value
let typeName = doxityProperty.outputs[0].type;
if (!_.isEmpty(doxityProperty.inputs)) {
// Properties never have more then a single input
typeName = `(${doxityProperty.inputs[0].type} => ${typeName})`;
}
const property = {
name: doxityProperty.name,
type: doxityUtils._convertType(typeName),
comment: doxityProperty.details,
};
return property;
});
const doxityEvents = _.filter(
doxityContractObj.abiDocs,
(abiDoc: DoxityAbiDoc) => abiDoc.type === AbiTypes.Event,
);
const events = _.map(doxityEvents, doxityEvent => {
const event = {
name: doxityEvent.name,
eventArgs: doxityUtils._convertEventArgs(doxityEvent.inputs),
};
return event;
});
const docSection: DocSection = {
comment: doxityContractObj.title,
constructors,
methods,
properties,
types: [],
functions: [],
events,
};
docAgnosticFormat[contractName] = docSection;
});
return docAgnosticFormat;
},
_convertParameters(inputs: DoxityInput[]): Parameter[] {
const parameters = _.map(inputs, input => {
const parameter = {
name: input.name,
comment: input.description,
isOptional: false,
type: doxityUtils._convertType(input.type),
};
return parameter;
});
return parameters;
},
_convertType(typeName: string): Type {
const type = {
name: typeName,
typeDocType: TypeDocTypes.Intrinsic,
};
return type;
},
_isMethod(abiDoc: DoxityAbiDoc): boolean {
if (abiDoc.type !== AbiTypes.Function) {
return false;
}
const hasInputs = !_.isEmpty(abiDoc.inputs);
const hasNamedOutputIfExists = !hasInputs || !_.isEmpty(abiDoc.inputs[0].name);
const isNameAllCaps = abiDoc.name === abiDoc.name.toUpperCase();
const isMethod = hasNamedOutputIfExists && !isNameAllCaps;
return isMethod;
},
_isProperty(abiDoc: DoxityAbiDoc): boolean {
if (abiDoc.type !== AbiTypes.Function) {
return false;
}
const hasInputs = !_.isEmpty(abiDoc.inputs);
const hasNamedOutputIfExists = !hasInputs || !_.isEmpty(abiDoc.inputs[0].name);
const isNameAllCaps = abiDoc.name === abiDoc.name.toUpperCase();
const isProperty = !hasNamedOutputIfExists || isNameAllCaps;
return isProperty;
},
_convertEventArgs(inputs: DoxityInput[]): EventArg[] {
const eventArgs = _.map(inputs, input => {
const eventArg = {
isIndexed: input.indexed,
name: input.name,
type: doxityUtils._convertType(input.type),
};
return eventArg;
});
return eventArgs;
},
};

View File

@@ -1,7 +1,3 @@
import { errorUtils } from '@0xproject/utils';
import * as _ from 'lodash';
import { DocsInfo } from '../docs_info';
import {
CustomType,
CustomTypeChild,
@@ -11,7 +7,6 @@ import {
ExternalTypeToLink,
GeneratedDocJson,
IndexSignature,
KindString,
Parameter,
Property,
Type,
@@ -21,7 +16,12 @@ import {
TypeParameter,
TypescriptFunction,
TypescriptMethod,
} from '../types';
} from '@0xproject/types';
import { errorUtils } from '@0xproject/utils';
import * as _ from 'lodash';
import { DocsInfo } from '../docs_info';
import { KindString } from '../types';
import { constants } from './constants';
@@ -471,6 +471,8 @@ export class TypeDocUtils {
methodIfExists = this._convertMethod(entity.declaration, isConstructor, sectionName);
} else if (entity.type === TypeDocTypes.Tuple) {
tupleElementsIfExists = _.map(entity.elements, el => {
// the following line is required due to an open tslint issue, https://github.com/palantir/tslint/issues/3540
// tslint:disable-next-line:no-unnecessary-type-assertion
return { name: el.name, typeDocType: el.type as TypeDocTypes };
});
}

View File

@@ -10,7 +10,7 @@ import {
} from '@0xproject/sol-resolver';
import { fetchAsync, logUtils } from '@0xproject/utils';
import chalk from 'chalk';
import { CompilerOptions, ContractArtifact, ContractVersionData } from 'ethereum-types';
import { CompilerOptions, ContractArtifact, ContractVersionData, StandardOutput } from 'ethereum-types';
import * as ethUtil from 'ethereumjs-util';
import * as fs from 'fs';
import * as _ from 'lodash';
@@ -94,7 +94,7 @@ export class Compiler {
if (await fsWrapper.doesFileExistAsync(compilerBinFilename)) {
solcjs = (await fsWrapper.readFileAsync(compilerBinFilename)).toString();
} else {
logUtils.log(`Downloading ${fullSolcVersion}...`);
logUtils.warn(`Downloading ${fullSolcVersion}...`);
const url = `${constants.BASE_COMPILER_URL}${fullSolcVersion}`;
const response = await fetchAsync(url);
const SUCCESS_STATUS = 200;
@@ -110,6 +110,21 @@ export class Compiler {
const solcInstance = solc.setupMethods(requireFromString(solcjs, compilerBinFilename));
return { solcInstance, fullSolcVersion };
}
private static _addHexPrefixToContractBytecode(compiledContract: solc.StandardContractOutput): void {
if (!_.isUndefined(compiledContract.evm)) {
if (!_.isUndefined(compiledContract.evm.bytecode) && !_.isUndefined(compiledContract.evm.bytecode.object)) {
compiledContract.evm.bytecode.object = ethUtil.addHexPrefix(compiledContract.evm.bytecode.object);
}
if (
!_.isUndefined(compiledContract.evm.deployedBytecode) &&
!_.isUndefined(compiledContract.evm.deployedBytecode.object)
) {
compiledContract.evm.deployedBytecode.object = ethUtil.addHexPrefix(
compiledContract.evm.deployedBytecode.object,
);
}
}
}
/**
* Instantiates a new instance of the Compiler class.
* @param opts Optional compiler options
@@ -144,22 +159,40 @@ export class Compiler {
public async compileAsync(): Promise<void> {
await createDirIfDoesNotExistAsync(this._artifactsDir);
await createDirIfDoesNotExistAsync(SOLC_BIN_DIR);
let contractNamesToCompile: string[] = [];
await this._compileContractsAsync(this._getContractNamesToCompile(), true);
}
/**
* Compiles Solidity files specified during instantiation, and returns the
* compiler output given by solc. Return value is an array of outputs:
* Solidity modules are batched together by version required, and each
* element of the returned array corresponds to a compiler version, and
* each element contains the output for all of the modules compiled with
* that version.
*/
public async getCompilerOutputsAsync(): Promise<StandardOutput[]> {
const promisedOutputs = this._compileContractsAsync(this._getContractNamesToCompile(), false);
return promisedOutputs;
}
private _getContractNamesToCompile(): string[] {
let contractNamesToCompile;
if (this._specifiedContracts === ALL_CONTRACTS_IDENTIFIER) {
const allContracts = this._nameResolver.getAll();
contractNamesToCompile = _.map(allContracts, contractSource =>
path.basename(contractSource.path, constants.SOLIDITY_FILE_EXTENSION),
);
} else {
contractNamesToCompile = this._specifiedContracts;
contractNamesToCompile = this._specifiedContracts.map(specifiedContract =>
path.basename(specifiedContract, constants.SOLIDITY_FILE_EXTENSION),
);
}
await this._compileContractsAsync(contractNamesToCompile);
return contractNamesToCompile;
}
/**
* Compiles contract and saves artifact to artifactsDir.
* Compiles contracts, and, if `shouldPersist` is true, saves artifacts to artifactsDir.
* @param fileName Name of contract with '.sol' extension.
* @return an array of compiler outputs, where each element corresponds to a different version of solc-js.
*/
private async _compileContractsAsync(contractNames: string[]): Promise<void> {
private async _compileContractsAsync(contractNames: string[], shouldPersist: boolean): Promise<StandardOutput[]> {
// batch input contracts together based on the version of the compiler that they require.
const versionToInputs: VersionToInputs = {};
@@ -200,10 +233,12 @@ export class Compiler {
versionToInputs[solcVersion].contractsToCompile.push(contractSource.path);
}
const compilerOutputs: StandardOutput[] = [];
const solcVersions = _.keys(versionToInputs);
for (const solcVersion of solcVersions) {
const input = versionToInputs[solcVersion];
logUtils.log(
logUtils.warn(
`Compiling ${input.contractsToCompile.length} contracts (${
input.contractsToCompile
}) with Solidity v${solcVersion}...`,
@@ -212,18 +247,34 @@ export class Compiler {
const { solcInstance, fullSolcVersion } = await Compiler._getSolcAsync(solcVersion);
const compilerOutput = this._compile(solcInstance, input.standardInput);
compilerOutputs.push(compilerOutput);
for (const contractPath of input.contractsToCompile) {
await this._verifyAndPersistCompiledContractAsync(
contractPath,
contractPathToData[contractPath].currentArtifactIfExists,
contractPathToData[contractPath].sourceTreeHashHex,
contractPathToData[contractPath].contractName,
fullSolcVersion,
compilerOutput,
);
const contractName = contractPathToData[contractPath].contractName;
const compiledContract = compilerOutput.contracts[contractPath][contractName];
if (_.isUndefined(compiledContract)) {
throw new Error(
`Contract ${contractName} not found in ${contractPath}. Please make sure your contract has the same name as it's file name`,
);
}
Compiler._addHexPrefixToContractBytecode(compiledContract);
if (shouldPersist) {
await this._persistCompiledContractAsync(
contractPath,
contractPathToData[contractPath].currentArtifactIfExists,
contractPathToData[contractPath].sourceTreeHashHex,
contractName,
fullSolcVersion,
compilerOutput,
);
}
}
}
return compilerOutputs;
}
private _shouldCompile(contractData: ContractData): boolean {
if (_.isUndefined(contractData.currentArtifactIfExists)) {
@@ -236,7 +287,7 @@ export class Compiler {
return !isUserOnLatestVersion || didCompilerSettingsChange || didSourceChange;
}
}
private async _verifyAndPersistCompiledContractAsync(
private async _persistCompiledContractAsync(
contractPath: string,
currentArtifactIfExists: ContractArtifact | void,
sourceTreeHashHex: string,
@@ -244,32 +295,13 @@ export class Compiler {
fullSolcVersion: string,
compilerOutput: solc.StandardOutput,
): Promise<void> {
const compiledData = compilerOutput.contracts[contractPath][contractName];
if (_.isUndefined(compiledData)) {
throw new Error(
`Contract ${contractName} not found in ${contractPath}. Please make sure your contract has the same name as it's file name`,
);
}
if (!_.isUndefined(compiledData.evm)) {
if (!_.isUndefined(compiledData.evm.bytecode) && !_.isUndefined(compiledData.evm.bytecode.object)) {
compiledData.evm.bytecode.object = ethUtil.addHexPrefix(compiledData.evm.bytecode.object);
}
if (
!_.isUndefined(compiledData.evm.deployedBytecode) &&
!_.isUndefined(compiledData.evm.deployedBytecode.object)
) {
compiledData.evm.deployedBytecode.object = ethUtil.addHexPrefix(
compiledData.evm.deployedBytecode.object,
);
}
}
const compiledContract = compilerOutput.contracts[contractPath][contractName];
const sourceCodes = _.mapValues(
compilerOutput.sources,
(_1, sourceFilePath) => this._resolver.resolve(sourceFilePath).source,
);
const contractVersion: ContractVersionData = {
compilerOutput: compiledData,
compilerOutput: compiledContract,
sources: compilerOutput.sources,
sourceCodes,
sourceTreeHashHex,
@@ -299,7 +331,7 @@ export class Compiler {
const artifactString = utils.stringifyWithFormatting(newArtifact);
const currentArtifactPath = `${this._artifactsDir}/${contractName}.json`;
await fsWrapper.writeFileAsync(currentArtifactPath, artifactString);
logUtils.log(`${contractName} artifact saved!`);
logUtils.warn(`${contractName} artifact saved!`);
}
private _compile(solcInstance: solc.SolcInstance, standardInput: solc.StandardInput): solc.StandardOutput {
const compiled: solc.StandardOutput = JSON.parse(
@@ -315,13 +347,13 @@ export class Compiler {
if (!_.isEmpty(errors)) {
errors.forEach(error => {
const normalizedErrMsg = getNormalizedErrMsg(error.formattedMessage || error.message);
logUtils.log(chalk.red(normalizedErrMsg));
logUtils.warn(chalk.red(normalizedErrMsg));
});
throw new Error('Compilation errors encountered');
} else {
warnings.forEach(warning => {
const normalizedWarningMsg = getNormalizedErrMsg(warning.formattedMessage || warning.message);
logUtils.log(chalk.yellow(normalizedWarningMsg));
logUtils.warn(chalk.yellow(normalizedWarningMsg));
});
}
}

View File

@@ -1,8 +1,28 @@
export { Compiler } from './compiler';
export {
AbiDefinition,
CompilerOptions,
CompilerSettings,
DataItem,
DevdocOutput,
ErrorSeverity,
ErrorType,
EventAbi,
EventParameter,
EvmBytecodeOutput,
EvmOutput,
FallbackAbi,
FunctionAbi,
MethodAbi,
ConstructorAbi,
ConstructorStateMutability,
ContractAbi,
OutputField,
CompilerSettingsMetadata,
OptimizerSettings,
ParamDescription,
SolcError,
StandardContractOutput,
StandardOutput,
StateMutability,
} from 'ethereum-types';

View File

@@ -26,7 +26,7 @@ export async function getContractArtifactIfExistsAsync(
contractArtifact = JSON.parse(contractArtifactString);
return contractArtifact;
} catch (err) {
logUtils.log(`Artifact for ${contractName} does not exist`);
logUtils.warn(`Artifact for ${contractName} does not exist`);
return undefined;
}
}
@@ -37,7 +37,7 @@ export async function getContractArtifactIfExistsAsync(
*/
export async function createDirIfDoesNotExistAsync(dirPath: string): Promise<void> {
if (!fsWrapper.doesPathExistSync(dirPath)) {
logUtils.log(`Creating directory at ${dirPath}...`);
logUtils.warn(`Creating directory at ${dirPath}...`);
await fsWrapper.mkdirpAsync(dirPath);
}
}

View File

@@ -0,0 +1,12 @@
[
{
"version": "1.0.0",
"changes": [
{
"note":
"Utility to generate documentation for Solidity smart contracts, outputting a format compliant with @0xproject/types.DocAgnosticFormat",
"pr": 1004
}
]
}
]

View File

View File

@@ -0,0 +1,49 @@
{
"name": "@0xproject/sol-doc",
"version": "1.0.0",
"description": "Solidity documentation generator",
"main": "lib/src/index.js",
"types": "lib/src/index.d.js",
"scripts": {
"build": "tsc",
"test": "mocha --require source-map-support/register --require make-promises-safe lib/test/**/*_test.js --timeout 6000 --exit",
"test:circleci": "yarn test:coverage",
"test:coverage": "nyc npm run test --all && yarn coverage:report:lcov",
"coverage:report:lcov": "nyc report --reporter=text-lcov > coverage/lcov.info",
"lint": "tslint --project . --format stylish",
"clean": "shx rm -rf lib",
"generate-v1-protocol-docs": "(cd ../contracts/src/1.0.0; node ../../../../node_modules/.bin/sol-doc --contracts-dir . --contracts Exchange/Exchange_v1.sol TokenRegistry/TokenRegistry.sol TokenTransferProxy/TokenTransferProxy_v1.sol) > v1.0.0.json",
"generate-v2-protocol-docs": "(cd ../contracts/src/2.0.0; node ../../../../node_modules/.bin/sol-doc --contracts-dir . --contracts $(cd protocol; ls -C1 */*.sol */interfaces/*.sol) ) > v2.0.0.json",
"deploy-v2-protocol-docs": "aws --profile 0xproject s3 cp --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers --content-type application/json v2.0.0.json s3://staging-doc-jsons/contracts/",
"deploy-v1-protocol-docs": "aws --profile 0xproject s3 cp --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers --content-type application/json v1.0.0.json s3://staging-doc-jsons/contracts/"
},
"bin": {
"sol-doc": "bin/sol-doc.js"
},
"repository": "https://github.com/0xProject/0x-monorepo.git",
"author": "F. Eugene Aumson",
"license": "Apache-2.0",
"dependencies": {
"@0xproject/sol-compiler": "^1.1.5",
"@0xproject/types": "^1.1.1",
"@0xproject/utils": "^1.0.11",
"ethereum-types": "^1.0.4",
"lodash": "^4.17.10",
"yargs": "^12.0.2"
},
"devDependencies": {
"@0xproject/tslint-config": "^1.0.7",
"chai": "^4.1.2",
"chai-as-promised": "^7.1.0",
"chai-bignumber": "^2.0.2",
"dirty-chai": "^2.0.1",
"make-promises-safe": "^1.1.0",
"mocha": "^5.2.0",
"shx": "^0.2.2",
"source-map-support": "^0.5.0",
"tslint": "5.11.0"
},
"publishConfig": {
"access": "public"
}
}

View File

@@ -0,0 +1,28 @@
import 'source-map-support/register';
import * as yargs from 'yargs';
import { logUtils } from '@0xproject/utils';
import { generateSolDocAsync } from './solidity_doc_generator';
const JSON_TAB_WIDTH = 4;
(async () => {
const argv = yargs
.option('contracts-dir', {
type: 'string',
description: 'path of contracts directory to compile',
})
.option('contracts', {
type: 'string',
description: 'comma separated list of contracts to compile',
})
.demandOption('contracts-dir')
.array('contracts')
.help().argv;
const doc = await generateSolDocAsync(argv.contractsDir, argv.contracts);
process.stdout.write(JSON.stringify(doc, null, JSON_TAB_WIDTH));
})().catch(err => {
logUtils.warn(err);
process.exit(1);
});

View File

@@ -0,0 +1 @@
export { generateSolDocAsync } from './solidity_doc_generator';

View File

@@ -0,0 +1,306 @@
import * as path from 'path';
import * as _ from 'lodash';
import {
AbiDefinition,
ConstructorAbi,
DataItem,
DevdocOutput,
EventAbi,
EventParameter,
FallbackAbi,
MethodAbi,
StandardContractOutput,
} from 'ethereum-types';
import { Compiler, CompilerOptions } from '@0xproject/sol-compiler';
import {
DocAgnosticFormat,
DocSection,
Event,
EventArg,
Parameter,
SolidityMethod,
Type,
TypeDocTypes,
} from '@0xproject/types';
/**
* Invoke the Solidity compiler and transform its ABI and devdoc outputs into a
* JSON format easily consumed by documentation rendering tools.
* @param contractsToDocument list of contracts for which to generate doc objects
* @param contractsDir the directory in which to find the `contractsToCompile` as well as their dependencies.
* @return doc object for use with documentation generation tools.
*/
export async function generateSolDocAsync(
contractsDir: string,
contractsToDocument?: string[],
): Promise<DocAgnosticFormat> {
const docWithDependencies: DocAgnosticFormat = {};
const compilerOptions = _makeCompilerOptions(contractsDir, contractsToDocument);
const compiler = new Compiler(compilerOptions);
const compilerOutputs = await compiler.getCompilerOutputsAsync();
for (const compilerOutput of compilerOutputs) {
const contractFileNames = _.keys(compilerOutput.contracts);
for (const contractFileName of contractFileNames) {
const contractNameToOutput = compilerOutput.contracts[contractFileName];
const contractNames = _.keys(contractNameToOutput);
for (const contractName of contractNames) {
const compiledContract = contractNameToOutput[contractName];
if (_.isUndefined(compiledContract.abi)) {
throw new Error('compiled contract did not contain ABI output');
}
docWithDependencies[contractName] = _genDocSection(compiledContract, contractName);
}
}
}
let doc: DocAgnosticFormat = {};
if (_.isUndefined(contractsToDocument) || contractsToDocument.length === 0) {
doc = docWithDependencies;
} else {
for (const contractToDocument of contractsToDocument) {
const contractBasename = path.basename(contractToDocument);
const contractName =
contractBasename.lastIndexOf('.sol') === -1
? contractBasename
: contractBasename.substring(0, contractBasename.lastIndexOf('.sol'));
doc[contractName] = docWithDependencies[contractName];
}
}
return doc;
}
function _makeCompilerOptions(contractsDir: string, contractsToCompile?: string[]): CompilerOptions {
const compilerOptions: CompilerOptions = {
contractsDir,
contracts: '*',
compilerSettings: {
outputSelection: {
['*']: {
['*']: ['abi', 'devdoc'],
},
},
},
};
const shouldOverrideCatchAllContractsConfig = !_.isUndefined(contractsToCompile) && contractsToCompile.length > 0;
if (shouldOverrideCatchAllContractsConfig) {
compilerOptions.contracts = contractsToCompile;
}
return compilerOptions;
}
function _genDocSection(compiledContract: StandardContractOutput, contractName: string): DocSection {
const docSection: DocSection = {
comment: _.isUndefined(compiledContract.devdoc) ? '' : compiledContract.devdoc.title,
constructors: [],
methods: [],
properties: [],
types: [],
functions: [],
events: [],
};
for (const abiDefinition of compiledContract.abi) {
switch (abiDefinition.type) {
case 'constructor':
docSection.constructors.push(_genConstructorDoc(contractName, abiDefinition, compiledContract.devdoc));
break;
case 'event':
(docSection.events as Event[]).push(_genEventDoc(abiDefinition));
// note that we're not sending devdoc to _genEventDoc().
// that's because the type of the events array doesn't have any fields for documentation!
break;
case 'function':
case 'fallback':
docSection.methods.push(_genMethodDoc(abiDefinition, compiledContract.devdoc));
break;
default:
throw new Error(
`unknown and unsupported AbiDefinition type '${(abiDefinition as AbiDefinition).type}'`,
);
}
}
return docSection;
}
function _genConstructorDoc(
contractName: string,
abiDefinition: ConstructorAbi,
devdocIfExists: DevdocOutput | undefined,
): SolidityMethod {
const { parameters, methodSignature } = _genMethodParamsDoc('', abiDefinition.inputs, devdocIfExists);
const comment = _devdocMethodDetailsIfExist(methodSignature, devdocIfExists);
const constructorDoc: SolidityMethod = {
isConstructor: true,
name: contractName,
callPath: '',
parameters,
returnType: { name: contractName, typeDocType: TypeDocTypes.Reference }, // sad we have to specify this
isConstant: false,
isPayable: abiDefinition.payable,
comment,
};
return constructorDoc;
}
function _devdocMethodDetailsIfExist(
methodSignature: string,
devdocIfExists: DevdocOutput | undefined,
): string | undefined {
let details;
if (!_.isUndefined(devdocIfExists)) {
const devdocMethodsIfExist = devdocIfExists.methods;
if (!_.isUndefined(devdocMethodsIfExist)) {
const devdocMethodIfExists = devdocMethodsIfExist[methodSignature];
if (!_.isUndefined(devdocMethodIfExists)) {
const devdocMethodDetailsIfExist = devdocMethodIfExists.details;
if (!_.isUndefined(devdocMethodDetailsIfExist)) {
details = devdocMethodDetailsIfExist;
}
}
}
}
return details;
}
function _genMethodDoc(
abiDefinition: MethodAbi | FallbackAbi,
devdocIfExists: DevdocOutput | undefined,
): SolidityMethod {
const name = abiDefinition.type === 'fallback' ? '' : abiDefinition.name;
const { parameters, methodSignature } =
abiDefinition.type === 'fallback'
? { parameters: [], methodSignature: `${name}()` }
: _genMethodParamsDoc(name, abiDefinition.inputs, devdocIfExists);
const comment = _devdocMethodDetailsIfExist(methodSignature, devdocIfExists);
const returnType =
abiDefinition.type === 'fallback'
? { name: '', typeDocType: TypeDocTypes.Intrinsic }
: _genMethodReturnTypeDoc(abiDefinition.outputs, methodSignature, devdocIfExists);
const returnComment =
_.isUndefined(devdocIfExists) || _.isUndefined(devdocIfExists.methods[methodSignature])
? undefined
: devdocIfExists.methods[methodSignature].return;
const isConstant = abiDefinition.type === 'fallback' ? true : abiDefinition.constant;
const methodDoc: SolidityMethod = {
isConstructor: false,
name,
callPath: '',
parameters,
returnType,
returnComment,
isConstant,
isPayable: abiDefinition.payable,
comment,
};
return methodDoc;
}
function _genEventDoc(abiDefinition: EventAbi): Event {
const eventDoc: Event = {
name: abiDefinition.name,
eventArgs: _genEventArgsDoc(abiDefinition.inputs, undefined),
};
return eventDoc;
}
function _genEventArgsDoc(args: EventParameter[], devdocIfExists: DevdocOutput | undefined): EventArg[] {
const eventArgsDoc: EventArg[] = [];
for (const arg of args) {
const name = arg.name;
const type: Type = {
name: arg.type,
typeDocType: TypeDocTypes.Intrinsic,
};
const eventArgDoc: EventArg = {
isIndexed: arg.indexed,
name,
type,
};
eventArgsDoc.push(eventArgDoc);
}
return eventArgsDoc;
}
/**
* Extract documentation for each method parameter from @param params.
*/
function _genMethodParamsDoc(
name: string,
abiParams: DataItem[],
devdocIfExists: DevdocOutput | undefined,
): { parameters: Parameter[]; methodSignature: string } {
const parameters: Parameter[] = [];
for (const abiParam of abiParams) {
const parameter: Parameter = {
name: abiParam.name,
comment: '',
isOptional: false, // Unsupported in Solidity, until resolution of https://github.com/ethereum/solidity/issues/232
type: { name: abiParam.type, typeDocType: TypeDocTypes.Intrinsic },
};
parameters.push(parameter);
}
const methodSignature = `${name}(${abiParams
.map(abiParam => {
return abiParam.type;
})
.join(',')})`;
if (!_.isUndefined(devdocIfExists)) {
const devdocMethodIfExists = devdocIfExists.methods[methodSignature];
if (!_.isUndefined(devdocMethodIfExists)) {
const devdocParamsIfExist = devdocMethodIfExists.params;
if (!_.isUndefined(devdocParamsIfExist)) {
for (const parameter of parameters) {
parameter.comment = devdocParamsIfExist[parameter.name];
}
}
}
}
return { parameters, methodSignature };
}
function _genMethodReturnTypeDoc(
outputs: DataItem[],
methodSignature: string,
devdocIfExists: DevdocOutput | undefined,
): Type {
const methodReturnTypeDoc: Type = {
name: '',
typeDocType: TypeDocTypes.Intrinsic,
tupleElements: undefined,
};
if (outputs.length > 1) {
methodReturnTypeDoc.typeDocType = TypeDocTypes.Tuple;
methodReturnTypeDoc.tupleElements = [];
for (const output of outputs) {
methodReturnTypeDoc.tupleElements.push({ name: output.type, typeDocType: TypeDocTypes.Intrinsic });
}
} else if (outputs.length === 1) {
methodReturnTypeDoc.typeDocType = TypeDocTypes.Intrinsic;
methodReturnTypeDoc.name = outputs[0].type;
}
return methodReturnTypeDoc;
}

View File

@@ -0,0 +1,7 @@
pragma solidity ^0.4.24;
contract MultipleReturnValues {
function methodWithMultipleReturnValues() public pure returns(int, int) {
return (0, 0);
}
}

View File

@@ -0,0 +1,40 @@
pragma solidity ^0.4.24;
/// @title Contract Title
/// @dev This is a very long documentation comment at the contract level.
/// It actually spans multiple lines, too.
contract NatspecEverything {
int d;
/// @dev Constructor @dev
/// @param p Constructor @param
constructor(int p) public { d = p; }
/// @notice publicMethod @notice
/// @dev publicMethod @dev
/// @param p publicMethod @param
/// @return publicMethod @return
function publicMethod(int p) public pure returns(int r) { return p; }
/// @dev Fallback @dev
function () public {}
/// @notice externalMethod @notice
/// @dev externalMethod @dev
/// @param p externalMethod @param
/// @return externalMethod @return
function externalMethod(int p) external pure returns(int r) { return p; }
/// @dev Here is a really long developer documentation comment, which spans
/// multiple lines, for the purposes of making sure that broken lines are
/// consolidated into one devdoc comment.
function methodWithLongDevdoc(int p) public pure returns(int) { return p; }
/// @dev AnEvent @dev
/// @param p on this event is an integer.
event AnEvent(int p);
/// @dev methodWithSolhintDirective @dev
// solhint-disable no-empty-blocks
function methodWithSolhintDirective() public pure {}
}

View File

@@ -0,0 +1,115 @@
/*
Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.4.14;
import { Ownable } from "zeppelin-solidity/contracts/ownership/Ownable.sol";
import { ERC20 as Token } from "zeppelin-solidity/contracts/token/ERC20/ERC20.sol";
/// @title TokenTransferProxy - Transfers tokens on behalf of contracts that have been approved via decentralized governance.
/// @author Amir Bandeali - <amir@0xProject.com>, Will Warren - <will@0xProject.com>
contract TokenTransferProxy is Ownable {
/// @dev Only authorized addresses can invoke functions with this modifier.
modifier onlyAuthorized {
require(authorized[msg.sender]);
_;
}
modifier targetAuthorized(address target) {
require(authorized[target]);
_;
}
modifier targetNotAuthorized(address target) {
require(!authorized[target]);
_;
}
mapping (address => bool) public authorized;
address[] public authorities;
event LogAuthorizedAddressAdded(address indexed target, address indexed caller);
event LogAuthorizedAddressRemoved(address indexed target, address indexed caller);
/*
* Public functions
*/
/// @dev Authorizes an address.
/// @param target Address to authorize.
function addAuthorizedAddress(address target)
public
onlyOwner
targetNotAuthorized(target)
{
authorized[target] = true;
authorities.push(target);
LogAuthorizedAddressAdded(target, msg.sender);
}
/// @dev Removes authorizion of an address.
/// @param target Address to remove authorization from.
function removeAuthorizedAddress(address target)
public
onlyOwner
targetAuthorized(target)
{
delete authorized[target];
for (uint i = 0; i < authorities.length; i++) {
if (authorities[i] == target) {
authorities[i] = authorities[authorities.length - 1];
authorities.length -= 1;
break;
}
}
LogAuthorizedAddressRemoved(target, msg.sender);
}
/// @dev Calls into ERC20 Token contract, invoking transferFrom.
/// @param token Address of token to transfer.
/// @param from Address to transfer token from.
/// @param to Address to transfer token to.
/// @param value Amount of token to transfer.
/// @return Success of transfer.
function transferFrom(
address token,
address from,
address to,
uint value)
public
onlyAuthorized
returns (bool)
{
return Token(token).transferFrom(from, to, value);
}
/*
* Public constant functions
*/
/// @dev Gets all authorized addresses.
/// @return Array of authorized addresses.
function getAuthorizedAddresses()
public
constant
returns (address[])
{
return authorities;
}
}

View File

@@ -0,0 +1,100 @@
/*
Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.4.14;
import { Ownable } from "zeppelin-solidity/contracts/ownership/Ownable.sol";
import { ERC20 as Token } from "zeppelin-solidity/contracts/token/ERC20/ERC20.sol";
contract TokenTransferProxyNoDevdoc is Ownable {
modifier onlyAuthorized {
require(authorized[msg.sender]);
_;
}
modifier targetAuthorized(address target) {
require(authorized[target]);
_;
}
modifier targetNotAuthorized(address target) {
require(!authorized[target]);
_;
}
mapping (address => bool) public authorized;
address[] public authorities;
event LogAuthorizedAddressAdded(address indexed target, address indexed caller);
event LogAuthorizedAddressRemoved(address indexed target, address indexed caller);
/*
* Public functions
*/
function addAuthorizedAddress(address target)
public
onlyOwner
targetNotAuthorized(target)
{
authorized[target] = true;
authorities.push(target);
LogAuthorizedAddressAdded(target, msg.sender);
}
function removeAuthorizedAddress(address target)
public
onlyOwner
targetAuthorized(target)
{
delete authorized[target];
for (uint i = 0; i < authorities.length; i++) {
if (authorities[i] == target) {
authorities[i] = authorities[authorities.length - 1];
authorities.length -= 1;
break;
}
}
LogAuthorizedAddressRemoved(target, msg.sender);
}
function transferFrom(
address token,
address from,
address to,
uint value)
public
onlyAuthorized
returns (bool)
{
return Token(token).transferFrom(from, to, value);
}
/*
* Public constant functions
*/
function getAuthorizedAddresses()
public
constant
returns (address[])
{
return authorities;
}
}

View File

@@ -0,0 +1,237 @@
import * as _ from 'lodash';
import * as chai from 'chai';
import 'mocha';
import { DocAgnosticFormat, Event, SolidityMethod } from '@0xproject/types';
import { generateSolDocAsync } from '../src/solidity_doc_generator';
import { chaiSetup } from './util/chai_setup';
chaiSetup.configure();
const expect = chai.expect;
describe('#SolidityDocGenerator', () => {
it('should generate a doc object that matches the devdoc-free TokenTransferProxy fixture', async () => {
const doc = await generateSolDocAsync(`${__dirname}/../../test/fixtures/contracts`, [
'TokenTransferProxyNoDevdoc',
]);
expect(doc).to.not.be.undefined();
verifyTokenTransferProxyABIIsDocumented(doc, 'TokenTransferProxyNoDevdoc');
});
const docPromises: Array<Promise<DocAgnosticFormat>> = [
generateSolDocAsync(`${__dirname}/../../test/fixtures/contracts`),
generateSolDocAsync(`${__dirname}/../../test/fixtures/contracts`, []),
];
docPromises.forEach(docPromise => {
it('should generate a doc object that matches the TokenTransferProxy fixture with its dependencies', async () => {
const doc = await docPromise;
expect(doc).to.not.be.undefined();
verifyTokenTransferProxyAndDepsABIsAreDocumented(doc, 'TokenTransferProxy');
let addAuthorizedAddressMethod: SolidityMethod | undefined;
for (const method of doc.TokenTransferProxy.methods) {
if (method.name === 'addAuthorizedAddress') {
addAuthorizedAddressMethod = method;
}
}
const tokenTransferProxyAddAuthorizedAddressComment = 'Authorizes an address.';
expect((addAuthorizedAddressMethod as SolidityMethod).comment).to.equal(
tokenTransferProxyAddAuthorizedAddressComment,
);
const expectedParamComment = 'Address to authorize.';
expect((addAuthorizedAddressMethod as SolidityMethod).parameters[0].comment).to.equal(expectedParamComment);
});
});
it('should generate a doc object that matches the TokenTransferProxy fixture', async () => {
const doc: DocAgnosticFormat = await generateSolDocAsync(`${__dirname}/../../test/fixtures/contracts`, [
'TokenTransferProxy',
]);
verifyTokenTransferProxyABIIsDocumented(doc, 'TokenTransferProxy');
});
describe('when processing all the permutations of devdoc stuff that we use in our contracts', () => {
let doc: DocAgnosticFormat;
before(async () => {
doc = await generateSolDocAsync(`${__dirname}/../../test/fixtures/contracts`, ['NatspecEverything']);
expect(doc).to.not.be.undefined();
expect(doc.NatspecEverything).to.not.be.undefined();
});
it('should emit the contract @title as its comment', () => {
expect(doc.NatspecEverything.comment).to.equal('Contract Title');
});
describe('should emit public method documentation for', () => {
let methodDoc: SolidityMethod;
before(() => {
// tslint:disable-next-line:no-unnecessary-type-assertion
methodDoc = doc.NatspecEverything.methods.find(method => {
return method.name === 'publicMethod';
}) as SolidityMethod;
if (_.isUndefined(methodDoc)) {
throw new Error('publicMethod not found');
}
});
it('method name', () => {
expect(methodDoc.name).to.equal('publicMethod');
});
it('method comment', () => {
expect(methodDoc.comment).to.equal('publicMethod @dev');
});
it('parameter name', () => {
expect(methodDoc.parameters[0].name).to.equal('p');
});
it('parameter comment', () => {
expect(methodDoc.parameters[0].comment).to.equal('publicMethod @param');
});
it('return type', () => {
expect(methodDoc.returnType.name).to.equal('int256');
});
it('return comment', () => {
expect(methodDoc.returnComment).to.equal('publicMethod @return');
});
});
describe('should emit external method documentation for', () => {
let methodDoc: SolidityMethod;
before(() => {
// tslint:disable-next-line:no-unnecessary-type-assertion
methodDoc = doc.NatspecEverything.methods.find(method => {
return method.name === 'externalMethod';
}) as SolidityMethod;
if (_.isUndefined(methodDoc)) {
throw new Error('externalMethod not found');
}
});
it('method name', () => {
expect(methodDoc.name).to.equal('externalMethod');
});
it('method comment', () => {
expect(methodDoc.comment).to.equal('externalMethod @dev');
});
it('parameter name', () => {
expect(methodDoc.parameters[0].name).to.equal('p');
});
it('parameter comment', () => {
expect(methodDoc.parameters[0].comment).to.equal('externalMethod @param');
});
it('return type', () => {
expect(methodDoc.returnType.name).to.equal('int256');
});
it('return comment', () => {
expect(methodDoc.returnComment).to.equal('externalMethod @return');
});
});
it('should not truncate a multi-line devdoc comment', () => {
// tslint:disable-next-line:no-unnecessary-type-assertion
const methodDoc: SolidityMethod = doc.NatspecEverything.methods.find(method => {
return method.name === 'methodWithLongDevdoc';
}) as SolidityMethod;
if (_.isUndefined(methodDoc)) {
throw new Error('methodWithLongDevdoc not found');
}
expect(methodDoc.comment).to.equal(
'Here is a really long developer documentation comment, which spans multiple lines, for the purposes of making sure that broken lines are consolidated into one devdoc comment.',
);
});
describe('should emit event documentation for', () => {
let eventDoc: Event;
before(() => {
eventDoc = (doc.NatspecEverything.events as Event[])[0];
});
it('event name', () => {
expect(eventDoc.name).to.equal('AnEvent');
});
it('parameter name', () => {
expect(eventDoc.eventArgs[0].name).to.equal('p');
});
});
it('should not let solhint directives obscure natspec content', () => {
// tslint:disable-next-line:no-unnecessary-type-assertion
const methodDoc: SolidityMethod = doc.NatspecEverything.methods.find(method => {
return method.name === 'methodWithSolhintDirective';
}) as SolidityMethod;
if (_.isUndefined(methodDoc)) {
throw new Error('methodWithSolhintDirective not found');
}
expect(methodDoc.comment).to.equal('methodWithSolhintDirective @dev');
});
});
it('should document a method that returns multiple values', async () => {
const doc = await generateSolDocAsync(`${__dirname}/../../test/fixtures/contracts`, ['MultipleReturnValues']);
expect(doc.MultipleReturnValues).to.not.be.undefined();
expect(doc.MultipleReturnValues.methods).to.not.be.undefined();
let methodWithMultipleReturnValues: SolidityMethod | undefined;
for (const method of doc.MultipleReturnValues.methods) {
if (method.name === 'methodWithMultipleReturnValues') {
methodWithMultipleReturnValues = method;
}
}
if (_.isUndefined(methodWithMultipleReturnValues)) {
throw new Error('method should not be undefined');
}
const returnType = methodWithMultipleReturnValues.returnType;
expect(returnType.typeDocType).to.equal('tuple');
if (_.isUndefined(returnType.tupleElements)) {
throw new Error('returnType.tupleElements should not be undefined');
}
expect(returnType.tupleElements.length).to.equal(2);
});
});
function verifyTokenTransferProxyABIIsDocumented(doc: DocAgnosticFormat, contractName: string): void {
expect(doc[contractName]).to.not.be.undefined();
expect(doc[contractName].constructors).to.not.be.undefined();
const tokenTransferProxyConstructorCount = 0;
const tokenTransferProxyMethodCount = 8;
const tokenTransferProxyEventCount = 3;
expect(doc[contractName].constructors.length).to.equal(tokenTransferProxyConstructorCount);
expect(doc[contractName].methods.length).to.equal(tokenTransferProxyMethodCount);
const events = doc[contractName].events;
if (_.isUndefined(events)) {
throw new Error('events should never be undefined');
}
expect(events.length).to.equal(tokenTransferProxyEventCount);
}
function verifyTokenTransferProxyAndDepsABIsAreDocumented(doc: DocAgnosticFormat, contractName: string): void {
verifyTokenTransferProxyABIIsDocumented(doc, contractName);
expect(doc.ERC20).to.not.be.undefined();
expect(doc.ERC20.constructors).to.not.be.undefined();
expect(doc.ERC20.methods).to.not.be.undefined();
const erc20ConstructorCount = 0;
const erc20MethodCount = 6;
const erc20EventCount = 2;
expect(doc.ERC20.constructors.length).to.equal(erc20ConstructorCount);
expect(doc.ERC20.methods.length).to.equal(erc20MethodCount);
if (_.isUndefined(doc.ERC20.events)) {
throw new Error('events should never be undefined');
}
expect(doc.ERC20.events.length).to.equal(erc20EventCount);
expect(doc.ERC20Basic).to.not.be.undefined();
expect(doc.ERC20Basic.constructors).to.not.be.undefined();
expect(doc.ERC20Basic.methods).to.not.be.undefined();
const erc20BasicConstructorCount = 0;
const erc20BasicMethodCount = 3;
const erc20BasicEventCount = 1;
expect(doc.ERC20Basic.constructors.length).to.equal(erc20BasicConstructorCount);
expect(doc.ERC20Basic.methods.length).to.equal(erc20BasicMethodCount);
if (_.isUndefined(doc.ERC20Basic.events)) {
throw new Error('events should never be undefined');
}
expect(doc.ERC20Basic.events.length).to.equal(erc20BasicEventCount);
let addAuthorizedAddressMethod: SolidityMethod | undefined;
for (const method of doc[contractName].methods) {
if (method.name === 'addAuthorizedAddress') {
addAuthorizedAddressMethod = method;
}
}
expect(
addAuthorizedAddressMethod,
`method addAuthorizedAddress not found in ${JSON.stringify(doc[contractName].methods)}`,
).to.not.be.undefined();
}

View File

@@ -0,0 +1,13 @@
import * as chai from 'chai';
import chaiAsPromised = require('chai-as-promised');
import ChaiBigNumber = require('chai-bignumber');
import * as dirtyChai from 'dirty-chai';
export const chaiSetup = {
configure(): void {
chai.config.includeStack = true;
chai.use(ChaiBigNumber());
chai.use(dirtyChai);
chai.use(chaiAsPromised);
},
};

View File

@@ -0,0 +1,8 @@
{
"extends": "../../tsconfig",
"compilerOptions": {
"outDir": "lib",
"rootDir": "."
},
"include": ["./src/**/*", "./test/**/*"]
}

View File

@@ -0,0 +1,3 @@
{
"extends": ["@0xproject/tslint-config"]
}

View File

@@ -158,6 +158,7 @@ export class Handler {
if (_.isUndefined(takerTokenIfExists)) {
throw new Error(`Unsupported asset type: ${takerTokenSymbol}`);
}
const makerAssetAmount = Web3Wrapper.toBaseUnitAmount(ASSET_AMOUNT, makerTokenIfExists.decimals);
const takerAssetAmount = Web3Wrapper.toBaseUnitAmount(ASSET_AMOUNT, takerTokenIfExists.decimals);
const makerAssetData = assetDataUtils.encodeERC20AssetData(makerTokenIfExists.address);

View File

@@ -1,3 +1,5 @@
// tslint:disable:max-file-line-count
import { BigNumber } from 'bignumber.js';
import { ContractAbi } from 'ethereum-types';
@@ -373,3 +375,226 @@ export interface PagedRequestOpts {
page?: number;
perPage?: number;
}
export interface TypeDocType {
type: TypeDocTypes;
value: string;
name: string;
types: TypeDocType[];
typeArguments?: TypeDocType[];
declaration: TypeDocNode;
elementType?: TypeDocType;
indexSignature?: TypeDocNode;
elements?: TupleElement[];
}
export interface TupleElement {
type: string;
name: string;
}
export interface TypeDocNode {
id?: number;
name?: string;
kind?: string;
defaultValue?: string;
kindString?: string;
type?: TypeDocType;
fileName?: string;
line?: number;
comment?: TypeDocNode;
text?: string;
shortText?: string;
returns?: string;
declaration: TypeDocNode;
flags?: TypeDocFlags;
indexSignature?: TypeDocNode;
signatures?: TypeDocNode[];
parameters?: TypeDocNode[];
typeParameter?: TypeDocNode[];
sources?: TypeDocNode[];
children?: TypeDocNode[];
groups?: TypeDocGroup[];
}
export interface TypeDocFlags {
isStatic?: boolean;
isOptional?: boolean;
isPublic?: boolean;
isExported?: boolean;
}
export interface TypeDocGroup {
title: string;
children: number[];
}
export enum TypeDocTypes {
Intrinsic = 'intrinsic',
Reference = 'reference',
Array = 'array',
StringLiteral = 'stringLiteral',
Reflection = 'reflection',
Union = 'union',
TypeParameter = 'typeParameter',
Intersection = 'intersection',
Tuple = 'tuple',
Unknown = 'unknown',
}
export interface CustomTypeChild {
name: string;
type?: Type;
defaultValue?: string;
}
export interface Event {
name: string;
eventArgs: EventArg[];
}
export interface EventArg {
isIndexed: boolean;
name: string;
type: Type;
}
export interface Property {
name: string;
type: Type;
source?: Source;
comment?: string;
callPath?: string;
}
export interface BaseMethod {
isConstructor: boolean;
name: string;
returnComment?: string | undefined;
callPath: string;
parameters: Parameter[];
returnType: Type;
comment?: string;
}
export interface BaseFunction {
name: string;
returnComment?: string | undefined;
parameters: Parameter[];
returnType: Type;
comment?: string;
}
export interface TypeDefinitionByName {
[typeName: string]: CustomType;
}
export interface DocAgnosticFormat {
[sectionName: string]: DocSection;
}
export interface DocSection {
comment: string;
constructors: Array<TypescriptMethod | SolidityMethod>;
methods: Array<TypescriptMethod | SolidityMethod>;
properties: Property[];
types: CustomType[];
functions: TypescriptFunction[];
events?: Event[];
externalExportToLink?: ExternalExportToLink;
}
export interface TypescriptMethod extends BaseMethod {
source?: Source;
isStatic?: boolean;
typeParameter?: TypeParameter;
}
export interface TypescriptFunction extends BaseFunction {
source?: Source;
typeParameter?: TypeParameter;
callPath: string;
}
export interface SolidityMethod extends BaseMethod {
isConstant?: boolean;
isPayable?: boolean;
}
export interface Source {
fileName: string;
line: number;
}
export interface Parameter {
name: string;
comment: string;
isOptional: boolean;
type: Type;
defaultValue?: string;
}
export interface TypeParameter {
name: string;
type: Type;
}
export interface Type {
name: string;
typeDocType: TypeDocTypes;
value?: string;
isExportedClassReference?: boolean;
typeArguments?: Type[];
elementType?: ElementType;
types?: Type[];
method?: TypescriptMethod;
indexSignature?: IndexSignature;
externalLink?: string;
tupleElements?: Type[];
}
export interface ElementType {
name: string;
typeDocType: TypeDocTypes;
}
export interface IndexSignature {
keyName: string;
keyType: Type;
valueName: string;
}
export interface CustomType {
name: string;
kindString: string;
type?: Type;
method?: TypescriptMethod;
indexSignature?: IndexSignature;
defaultValue?: string;
comment?: string;
children?: CustomTypeChild[];
}
export interface GeneratedDocJson {
version: string;
metadata: Metadata;
typedocJson: TypeDocNode;
}
export interface ExportNameToTypedocNames {
[exportName: string]: string[];
}
export interface ExternalTypeToLink {
[externalTypeName: string]: string;
}
export interface ExternalExportToLink {
[externalExport: string]: string;
}
export interface Metadata {
exportPathToTypedocNames: ExportNameToTypedocNames;
exportPathOrder: string[];
externalTypeToLink: ExternalTypeToLink;
externalExportToLink: ExternalExportToLink;
}

View File

@@ -1,4 +1,6 @@
declare module 'solc' {
export { ErrorType, ErrorSeverity, SolcError, StandardContractOutput, StandardOutput } from 'ethereum-types';
import { SolcError } from 'ethereum-types';
export interface ContractCompilationResult {
srcmap: string;
srcmapRuntime: string;
@@ -87,62 +89,6 @@ declare module 'solc' {
};
settings: CompilerSettings;
}
export type ErrorType =
| 'JSONError'
| 'IOError'
| 'ParserError'
| 'DocstringParsingError'
| 'SyntaxError'
| 'DeclarationError'
| 'TypeError'
| 'UnimplementedFeatureError'
| 'InternalCompilerError'
| 'Exception'
| 'CompilerError'
| 'FatalError'
| 'Warning';
export type ErrorSeverity = 'error' | 'warning';
export interface Error {
sourceLocation?: {
file: string;
start: number;
end: number;
};
type: ErrorType;
component: 'general' | 'ewasm';
severity: ErrorSeverity;
message: string;
formattedMessage?: string;
}
import { ContractAbi } from 'ethereum-types';
export interface StandardContractOutput {
abi: ContractAbi;
evm: {
bytecode: {
object: string;
sourceMap: string;
};
deployedBytecode: {
object: string;
sourceMap: string;
};
};
}
export interface StandardOutput {
errors: Error[];
sources: {
[fileName: string]: {
id: number;
ast?: object;
legacyAST?: object;
};
};
contracts: {
[fileName: string]: {
[contractName: string]: StandardContractOutput;
};
};
}
export interface SolcInstance {
compile(
sources: InputSources,
@@ -151,6 +97,9 @@ declare module 'solc' {
): CompilationResult;
compileStandardWrapper(input: string, findImports: (importPath: string) => ImportContents): string;
}
export function loadRemoteVersion(versionName: string, cb: (err: Error | null, res?: SolcInstance) => void): void;
export function loadRemoteVersion(
versionName: string,
cb: (err: SolcError | null, res?: SolcInstance) => void,
): void;
export function setupMethods(solcBin: any): SolcInstance;
}

View File

@@ -1,8 +1 @@
Welcome to the [0x smart contracts](https://github.com/0xProject/contracts) documentation! This documentation is intended for dApp developers who want to integrate 0x exchange functionality directly into their own smart contracts.
### Helpful wiki articles:
* [Overview of 0x protocol architecture](https://0xproject.com/wiki#Architecture)
* [0x smart contract interactions](https://0xproject.com/wiki#Contract-Interactions)
* [Deployed smart contract addresses](https://0xproject.com/wiki#Deployed-Addresses)
* [0x protocol message format](https://0xproject.com/wiki#Message-Format)
Welcome to the [0x smart contracts](https://github.com/0xProject/0x-monorepo/tree/development/packages/contracts) documentation! This documentation is intended for dApp developers who want to integrate 0x exchange functionality directly into their own smart contracts.

View File

@@ -0,0 +1,6 @@
Welcome to the [0x smart contracts](https://github.com/0xProject/0x-monorepo/tree/development/packages/contracts) documentation! This documentation is intended for dApp developers who want to integrate 0x exchange functionality directly into their own smart contracts.
### Helpful wiki articles:
* [Deployed smart contract addresses](https://0xproject.com/wiki#Deployed-Addresses)
* [0x Protocol Specification](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md)

View File

@@ -11,29 +11,28 @@ import { Translate } from 'ts/utils/translate';
/* tslint:disable:no-var-requires */
const IntroMarkdownV1 = require('md/docs/smart_contracts/1.0.0/introduction');
const IntroMarkdownV2 = require('md/docs/smart_contracts/2.0.0/introduction');
/* tslint:enable:no-var-requires */
const docsInfoConfig: DocsInfoConfig = {
id: DocPackages.SmartContracts,
packageName: 'contracts',
type: SupportedDocJson.Doxity,
type: SupportedDocJson.SolDoc,
displayName: '0x Smart Contracts',
packageUrl: 'https://github.com/0xProject/contracts',
markdownMenu: {
introduction: [Sections.Introduction],
contracts: [Sections.Exchange, Sections.TokenRegistry, Sections.ZRXToken, Sections.TokenTransferProxy],
},
sectionNameToMarkdownByVersion: {
'0.0.1': {
[Sections.Introduction]: IntroMarkdownV1,
},
'2.0.0': {
[Sections.Introduction]: IntroMarkdownV2,
},
},
markdownSections: {
Introduction: Sections.Introduction,
Exchange: Sections.Exchange,
TokenTransferProxy: Sections.TokenTransferProxy,
TokenRegistry: Sections.TokenRegistry,
ZRXToken: Sections.ZRXToken,
},
contractsByVersionByNetworkId: {
'1.0.0': {

View File

@@ -1,4 +1,11 @@
import { DocAgnosticFormat, DocsInfo, Documentation } from '@0xproject/react-docs';
import {
DocAgnosticFormat,
DocsInfo,
Documentation,
GeneratedDocJson,
SupportedDocJson,
TypeDocUtils,
} from '@0xproject/react-docs';
import findVersions = require('find-versions');
import * as _ from 'lodash';
import * as React from 'react';
@@ -128,7 +135,22 @@ export class DocPage extends React.Component<DocPageProps, DocPageState> {
const versionFilePathToFetch = versionToFilePath[versionToFetch];
const versionDocObj = await docUtils.getJSONDocFileAsync(versionFilePathToFetch, docBucketRoot);
const docAgnosticFormat = this.props.docsInfo.convertToDocAgnosticFormat(versionDocObj);
let docAgnosticFormat;
if (this.props.docsInfo.type === SupportedDocJson.TypeDoc) {
docAgnosticFormat = new TypeDocUtils(
versionDocObj as GeneratedDocJson,
this.props.docsInfo,
).convertToDocAgnosticFormat();
} else if (this.props.docsInfo.type === SupportedDocJson.SolDoc) {
// documenting solidity.
docAgnosticFormat = versionDocObj as DocAgnosticFormat;
// HACK: need to modify docsInfo like convertToDocAgnosticFormat() would do
this.props.docsInfo.menu.Contracts = [];
_.each(docAgnosticFormat, (docObj, contractName) => {
this.props.docsInfo.sections[contractName] = contractName;
this.props.docsInfo.menu.Contracts.push(contractName);
});
}
if (!this._isUnmounted) {
this.setState({

View File

@@ -1,4 +1,4 @@
import { DoxityDocObj, GeneratedDocJson } from '@0xproject/react-docs';
import { DocAgnosticFormat, GeneratedDocJson } from '@0xproject/react-docs';
import { fetchAsync, logUtils } from '@0xproject/utils';
import * as _ from 'lodash';
import { S3FileObject, VersionToFilePath } from 'ts/types';
@@ -70,7 +70,7 @@ export const docUtils = {
});
return versionFilePaths;
},
async getJSONDocFileAsync(filePath: string, s3DocJsonRoot: string): Promise<GeneratedDocJson | DoxityDocObj> {
async getJSONDocFileAsync(filePath: string, s3DocJsonRoot: string): Promise<GeneratedDocJson | DocAgnosticFormat> {
const endpoint = `${s3DocJsonRoot}/${filePath}`;
const response = await fetchAsync(endpoint);
if (response.status !== 200) {