Merge branch 'development' into breakUp0xjs
* development: Fix ganache subprovider config Fix a bug in compiler config precedence Fix linter errors Fix templates Remove unused deployer docs configs Add a legacy endpoint for the deployer Add a check for compiler output Add a comment Put ARTIFACTS_VERSION in a config Improve a comment Remove _applyDefaultsToDeployTxDataAsync Add a HACK comment Fix linter issues Rename deployer to sol-compiler Remove deployer Remove deployer from 0x.js and migrations Configure migrations with a compiler.json Remove deployer from metacoin and contract tests Update wallet footer and add remove token functionality # Conflicts: # .gitignore # packages/0x.js/package.json # packages/0x.js/src/0x.ts # packages/contracts/package.json # packages/contracts/test/multi_sig_with_time_lock.ts # packages/contracts/test/multi_sig_with_time_lock_except_remove_auth_addr.ts # packages/contracts/util/artifacts.ts # packages/deployer/test/deployer_test.ts # packages/migrations/package.json
This commit is contained in:
7
packages/sol-compiler/.npmignore
Normal file
7
packages/sol-compiler/.npmignore
Normal file
@@ -0,0 +1,7 @@
|
||||
.*
|
||||
yarn-error.log
|
||||
/src/
|
||||
/scripts/
|
||||
test/
|
||||
tsconfig.json
|
||||
/lib/src/monorepo_scripts/
|
||||
151
packages/sol-compiler/CHANGELOG.json
Normal file
151
packages/sol-compiler/CHANGELOG.json
Normal file
@@ -0,0 +1,151 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1525477860,
|
||||
"version": "0.4.3",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "0.4.2",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add support for solidity 0.4.23",
|
||||
"pr": 545
|
||||
}
|
||||
],
|
||||
"timestamp": 1525428773
|
||||
},
|
||||
{
|
||||
"version": "0.4.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add support for solidity 0.4.22",
|
||||
"pr": 531
|
||||
}
|
||||
],
|
||||
"timestamp": 1524044013
|
||||
},
|
||||
{
|
||||
"version": "0.4.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Changed the config key `web3Provider` to `provider` to be consistent with other tools",
|
||||
"pr": 501
|
||||
}
|
||||
],
|
||||
"timestamp": 1523462196
|
||||
},
|
||||
{
|
||||
"version": "0.3.5",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Don't try to write contract artifact if an error occured",
|
||||
"pr": 485
|
||||
}
|
||||
],
|
||||
"timestamp": 1522673609
|
||||
},
|
||||
{
|
||||
"version": "0.3.4",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Create solc_bin directory if does not exist before attempting to compile",
|
||||
"pr": 491
|
||||
}
|
||||
],
|
||||
"timestamp": 1522658513
|
||||
},
|
||||
{
|
||||
"version": "0.3.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add TS types for `yargs`"
|
||||
}
|
||||
],
|
||||
"timestamp": 1521298800
|
||||
},
|
||||
{
|
||||
"version": "0.3.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add support for Solidity 0.4.20 and 0.4.21"
|
||||
},
|
||||
{
|
||||
"note": "Replace `jsonrpcPort` config with `jsonrpcUrl`",
|
||||
"pr": 426
|
||||
},
|
||||
{
|
||||
"note": "Replace `jsonrpc-port` CLI option with `jsonrpc-url`",
|
||||
"pr": 426
|
||||
},
|
||||
{
|
||||
"note": "Export the `Compiler`",
|
||||
"pr": 426
|
||||
},
|
||||
{
|
||||
"note": "Load solc from remote source instead of having it locally",
|
||||
"pr": 426
|
||||
},
|
||||
{
|
||||
"note":
|
||||
"Add `bytecode`, `runtime_bytecode`, `source_map`, `source_map_runtime` and `sources` fields to artifacts",
|
||||
"pr": 426
|
||||
},
|
||||
{
|
||||
"note": "Remove 0x-specific `migrate` command",
|
||||
"pr": 426
|
||||
},
|
||||
{
|
||||
"note":
|
||||
"Allow deployer to accept a provider instead of port and host. This makes it possible to run it with in-process ganache-core",
|
||||
"pr": 426
|
||||
},
|
||||
{
|
||||
"note": "Consolidate all `console.log` calls into `logUtils` in the `@0xproject/utils` package",
|
||||
"pr": 452
|
||||
},
|
||||
{
|
||||
"note": "Add `#!/usr/bin/env node` pragma above `cli.ts` script to fix command-line error."
|
||||
}
|
||||
],
|
||||
"timestamp": 1521298800
|
||||
},
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Check dependencies when determining if contracts should be recompiled",
|
||||
"pr": 408
|
||||
},
|
||||
{
|
||||
"note":
|
||||
"Improve an error message for when deployer is supplied with an incorrect number of constructor arguments",
|
||||
"pr": 419
|
||||
}
|
||||
],
|
||||
"timestamp": 1520089200
|
||||
},
|
||||
{
|
||||
"version": "0.1.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add the ability to pass in specific contracts to compile in CLI",
|
||||
"pr": 400
|
||||
}
|
||||
],
|
||||
"timestamp": 1518706800
|
||||
},
|
||||
{
|
||||
"version": "0.0.8",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Fix publishing issue where .npmignore was not properly excluding undesired content",
|
||||
"pr": 389
|
||||
}
|
||||
],
|
||||
"timestamp": 1518102000
|
||||
}
|
||||
]
|
||||
60
packages/sol-compiler/CHANGELOG.md
Normal file
60
packages/sol-compiler/CHANGELOG.md
Normal file
@@ -0,0 +1,60 @@
|
||||
<!--
|
||||
This file is auto-generated using the monorepo-scripts package. Don't edit directly.
|
||||
Edit the package's CHANGELOG.json file only.
|
||||
-->
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v0.4.3 - _May 5, 2018_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v0.4.2 - _May 4, 2018_
|
||||
|
||||
* Add support for solidity 0.4.23 (#545)
|
||||
|
||||
## v0.4.1 - _April 18, 2018_
|
||||
|
||||
* Add support for solidity 0.4.22 (#531)
|
||||
|
||||
## v0.4.0 - _April 11, 2018_
|
||||
|
||||
* Changed the config key `web3Provider` to `provider` to be consistent with other tools (#501)
|
||||
|
||||
## v0.3.5 - _April 2, 2018_
|
||||
|
||||
* Don't try to write contract artifact if an error occured (#485)
|
||||
|
||||
## v0.3.4 - _April 2, 2018_
|
||||
|
||||
* Create solc_bin directory if does not exist before attempting to compile (#491)
|
||||
|
||||
## v0.3.1 - _March 17, 2018_
|
||||
|
||||
* Add TS types for `yargs`
|
||||
|
||||
## v0.3.0 - _March 17, 2018_
|
||||
|
||||
* Add support for Solidity 0.4.20 and 0.4.21
|
||||
* Replace `jsonrpcPort` config with `jsonrpcUrl` (#426)
|
||||
* Replace `jsonrpc-port` CLI option with `jsonrpc-url` (#426)
|
||||
* Export the `Compiler` (#426)
|
||||
* Load solc from remote source instead of having it locally (#426)
|
||||
* Add `bytecode`, `runtime_bytecode`, `source_map`, `source_map_runtime` and `sources` fields to artifacts (#426)
|
||||
* Remove 0x-specific `migrate` command (#426)
|
||||
* Allow deployer to accept a provider instead of port and host. This makes it possible to run it with in-process ganache-core (#426)
|
||||
* Consolidate all `console.log` calls into `logUtils` in the `@0xproject/utils` package (#452)
|
||||
* Add `#!/usr/bin/env node` pragma above `cli.ts` script to fix command-line error.
|
||||
|
||||
## v0.2.0 - _March 3, 2018_
|
||||
|
||||
* Check dependencies when determining if contracts should be recompiled (#408)
|
||||
* Improve an error message for when deployer is supplied with an incorrect number of constructor arguments (#419)
|
||||
|
||||
## v0.1.0 - _February 15, 2018_
|
||||
|
||||
* Add the ability to pass in specific contracts to compile in CLI (#400)
|
||||
|
||||
## v0.0.8 - _February 8, 2018_
|
||||
|
||||
* Fix publishing issue where .npmignore was not properly excluding undesired content (#389)
|
||||
103
packages/sol-compiler/README.md
Normal file
103
packages/sol-compiler/README.md
Normal file
@@ -0,0 +1,103 @@
|
||||
## @0xproject/sol-compiler
|
||||
|
||||
This repository contains a CLI tool that facilitates compiling smart contracts.
|
||||
|
||||
### Read the [Documentation](https://0xproject.com/docs/sol-compiler).
|
||||
|
||||
## Installation
|
||||
|
||||
#### CLI Installation
|
||||
|
||||
```bash
|
||||
yarn global add @0xproject/sol-compiler
|
||||
```
|
||||
|
||||
#### API Installation
|
||||
|
||||
```bash
|
||||
yarn add @0xproject/sol-compiler
|
||||
```
|
||||
|
||||
If your project is in [TypeScript](https://www.typescriptlang.org/), add the following to your `tsconfig.json`:
|
||||
|
||||
```json
|
||||
"compilerOptions": {
|
||||
"typeRoots": ["node_modules/@0xproject/typescript-typings/types", "node_modules/@types"],
|
||||
}
|
||||
```
|
||||
|
||||
**Import**
|
||||
|
||||
```typescript
|
||||
import { Compiler } from '@0xproject/sol-compiler';
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```javascript
|
||||
var Compiler = require('@0xproject/sol-compiler').Compiler;
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
We welcome improvements and fixes from the wider community! To report bugs within this package, please create an issue in this repository.
|
||||
|
||||
Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started.
|
||||
|
||||
### Install dependencies
|
||||
|
||||
If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them:
|
||||
|
||||
```bash
|
||||
yarn config set workspaces-experimental true
|
||||
```
|
||||
|
||||
Then install dependencies
|
||||
|
||||
```bash
|
||||
yarn install
|
||||
```
|
||||
|
||||
### Build
|
||||
|
||||
If this is your **first** time building this package, you must first build **all** packages within the monorepo. This is because packages that depend on other packages located inside this monorepo are symlinked when run from **within** the monorepo. This allows you to make changes across multiple packages without first publishing dependent packages to NPM. To build all packages, run the following from the monorepo root directory:
|
||||
|
||||
```bash
|
||||
yarn lerna:rebuild
|
||||
```
|
||||
|
||||
Or continuously rebuild on change:
|
||||
|
||||
```bash
|
||||
yarn dev
|
||||
```
|
||||
|
||||
You can also build this specific package by running the following from within its directory:
|
||||
|
||||
```bash
|
||||
yarn build
|
||||
```
|
||||
|
||||
or continuously rebuild on change:
|
||||
|
||||
```bash
|
||||
yarn build:watch
|
||||
```
|
||||
|
||||
### Clean
|
||||
|
||||
```bash
|
||||
yarn clean
|
||||
```
|
||||
|
||||
### Lint
|
||||
|
||||
```bash
|
||||
yarn lint
|
||||
```
|
||||
|
||||
### Run Tests
|
||||
|
||||
```bash
|
||||
yarn test
|
||||
```
|
||||
0
packages/sol-compiler/coverage/.gitkeep
Normal file
0
packages/sol-compiler/coverage/.gitkeep
Normal file
93
packages/sol-compiler/package.json
Normal file
93
packages/sol-compiler/package.json
Normal file
@@ -0,0 +1,93 @@
|
||||
{
|
||||
"name": "@0xproject/sol-compiler",
|
||||
"version": "0.4.3",
|
||||
"description": "Solidity compiler wrapper and artifactor",
|
||||
"main": "lib/src/index.js",
|
||||
"types": "lib/src/index.d.ts",
|
||||
"scripts": {
|
||||
"build:watch": "tsc -w",
|
||||
"build": "yarn clean && copyfiles 'test/fixtures/contracts/**/*' ./lib && tsc && copyfiles -u 3 './lib/src/monorepo_scripts/**/*' ./scripts",
|
||||
"test": "run-s build run_mocha",
|
||||
"run_mocha": "mocha lib/test/*_test.js --bail --exit",
|
||||
"test:coverage": "nyc npm run test --all && yarn coverage:report:lcov",
|
||||
"coverage:report:lcov": "nyc report --reporter=text-lcov > coverage/lcov.info",
|
||||
"clean": "shx rm -rf lib scripts",
|
||||
"migrate": "npm run build; node lib/src/cli.js migrate",
|
||||
"lint": "tslint --project .",
|
||||
"test:circleci": "yarn test:coverage",
|
||||
"docs:stage": "yarn build && node ./scripts/stage_docs.js",
|
||||
"manual:postpublish": "yarn build; node ./scripts/postpublish.js",
|
||||
"docs:json": "typedoc --excludePrivate --excludeExternals --target ES5 --json $JSON_FILE_PATH $PROJECT_FILES",
|
||||
"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"
|
||||
},
|
||||
"config": {
|
||||
"postpublish": {
|
||||
"assets": [],
|
||||
"docPublishConfigs": {
|
||||
"extraFileIncludes": [
|
||||
"../types/src/index.ts"
|
||||
],
|
||||
"s3BucketPath": "s3://doc-jsons/sol-compiler/",
|
||||
"s3StagingBucketPath": "s3://staging-doc-jsons/sol-compiler/"
|
||||
}
|
||||
}
|
||||
},
|
||||
"bin": {
|
||||
"sol-compiler": "lib/src/cli.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/0xProject/0x-monorepo.git"
|
||||
},
|
||||
"author": "Amir Bandeali",
|
||||
"license": "Apache-2.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/0xProject/0x-monorepo/issues"
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/packages/sol-compiler/README.md",
|
||||
"devDependencies": {
|
||||
"@0xproject/dev-utils": "^0.4.1",
|
||||
"@0xproject/monorepo-scripts": "^0.1.19",
|
||||
"@0xproject/tslint-config": "^0.4.17",
|
||||
"@types/mkdirp": "^0.5.2",
|
||||
"@types/require-from-string": "^1.2.0",
|
||||
"@types/semver": "^5.5.0",
|
||||
"chai": "^4.0.1",
|
||||
"chai-as-promised": "^7.1.0",
|
||||
"copyfiles": "^1.2.0",
|
||||
"dirty-chai": "^2.0.1",
|
||||
"mocha": "^4.0.1",
|
||||
"npm-run-all": "^4.1.2",
|
||||
"nyc": "^11.0.1",
|
||||
"shx": "^0.2.2",
|
||||
"tslint": "5.8.0",
|
||||
"typedoc": "0xProject/typedoc",
|
||||
"types-bn": "^0.0.1",
|
||||
"typescript": "2.7.1",
|
||||
"web3-typescript-typings": "^0.10.2",
|
||||
"zeppelin-solidity": "1.8.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0xproject/json-schemas": "^0.7.23",
|
||||
"@0xproject/sol-resolver": "^0.0.4",
|
||||
"@0xproject/types": "^0.6.3",
|
||||
"@0xproject/typescript-typings": "^0.3.1",
|
||||
"@0xproject/utils": "^0.6.1",
|
||||
"@0xproject/web3-wrapper": "^0.6.3",
|
||||
"@types/yargs": "^11.0.0",
|
||||
"chalk": "^2.3.0",
|
||||
"ethereumjs-util": "^5.1.1",
|
||||
"isomorphic-fetch": "^2.2.1",
|
||||
"lodash": "^4.17.4",
|
||||
"mkdirp": "^0.5.1",
|
||||
"require-from-string": "^2.0.1",
|
||||
"semver": "^5.5.0",
|
||||
"solc": "^0.4.23",
|
||||
"web3": "^0.20.0",
|
||||
"web3-eth-abi": "^1.0.0-beta.24",
|
||||
"yargs": "^10.0.3"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
||||
0
packages/sol-compiler/solc_bin/.gitkeep
Normal file
0
packages/sol-compiler/solc_bin/.gitkeep
Normal file
46
packages/sol-compiler/src/cli.ts
Normal file
46
packages/sol-compiler/src/cli.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env node
|
||||
// We need the above pragma since this script will be run as a command-line tool.
|
||||
|
||||
import { BigNumber, logUtils } from '@0xproject/utils';
|
||||
import { Web3Wrapper } from '@0xproject/web3-wrapper';
|
||||
import * as _ from 'lodash';
|
||||
import * as path from 'path';
|
||||
import * as Web3 from 'web3';
|
||||
import * as yargs from 'yargs';
|
||||
|
||||
import { Compiler } from './compiler';
|
||||
import { constants } from './utils/constants';
|
||||
import { CompilerOptions } from './utils/types';
|
||||
|
||||
const DEFAULT_CONTRACTS_LIST = '*';
|
||||
const SEPARATOR = ',';
|
||||
|
||||
(async () => {
|
||||
const argv = yargs
|
||||
.option('contracts-dir', {
|
||||
type: 'string',
|
||||
description: 'path of contracts directory to compile',
|
||||
})
|
||||
.option('artifacts-dir', {
|
||||
type: 'string',
|
||||
description: 'path to write contracts artifacts to',
|
||||
})
|
||||
.option('contracts', {
|
||||
type: 'string',
|
||||
description: 'comma separated list of contracts to compile',
|
||||
})
|
||||
.help().argv;
|
||||
const contracts = _.isUndefined(argv.contracts)
|
||||
? undefined
|
||||
: argv.contracts === DEFAULT_CONTRACTS_LIST ? DEFAULT_CONTRACTS_LIST : argv.contracts.split(SEPARATOR);
|
||||
const opts: CompilerOptions = {
|
||||
contractsDir: argv.contractsDir,
|
||||
artifactsDir: argv.artifactsDir,
|
||||
contracts,
|
||||
};
|
||||
const compiler = new Compiler(opts);
|
||||
await compiler.compileAsync();
|
||||
})().catch(err => {
|
||||
logUtils.log(err);
|
||||
process.exit(1);
|
||||
});
|
||||
276
packages/sol-compiler/src/compiler.ts
Normal file
276
packages/sol-compiler/src/compiler.ts
Normal file
@@ -0,0 +1,276 @@
|
||||
import {
|
||||
ContractSource,
|
||||
ContractSources,
|
||||
EnumerableResolver,
|
||||
FallthroughResolver,
|
||||
FSResolver,
|
||||
NameResolver,
|
||||
NPMResolver,
|
||||
RelativeFSResolver,
|
||||
Resolver,
|
||||
URLResolver,
|
||||
} from '@0xproject/sol-resolver';
|
||||
import { ContractAbi } from '@0xproject/types';
|
||||
import { logUtils, promisify } from '@0xproject/utils';
|
||||
import chalk from 'chalk';
|
||||
import * as ethUtil from 'ethereumjs-util';
|
||||
import * as fs from 'fs';
|
||||
import 'isomorphic-fetch';
|
||||
import * as _ from 'lodash';
|
||||
import * as path from 'path';
|
||||
import * as requireFromString from 'require-from-string';
|
||||
import * as semver from 'semver';
|
||||
import solc = require('solc');
|
||||
|
||||
import { binPaths } from './solc/bin_paths';
|
||||
import {
|
||||
createDirIfDoesNotExistAsync,
|
||||
getContractArtifactIfExistsAsync,
|
||||
getNormalizedErrMsg,
|
||||
parseDependencies,
|
||||
parseSolidityVersionRange,
|
||||
} from './utils/compiler';
|
||||
import { constants } from './utils/constants';
|
||||
import { fsWrapper } from './utils/fs_wrapper';
|
||||
import {
|
||||
CompilerOptions,
|
||||
ContractArtifact,
|
||||
ContractNetworkData,
|
||||
ContractNetworks,
|
||||
ContractSourceData,
|
||||
ContractSpecificSourceData,
|
||||
ContractVersionData,
|
||||
} from './utils/types';
|
||||
import { utils } from './utils/utils';
|
||||
|
||||
type TYPE_ALL_FILES_IDENTIFIER = '*';
|
||||
const ALL_CONTRACTS_IDENTIFIER = '*';
|
||||
const ALL_FILES_IDENTIFIER = '*';
|
||||
const SOLC_BIN_DIR = path.join(__dirname, '..', '..', 'solc_bin');
|
||||
const DEFAULT_CONTRACTS_DIR = path.resolve('contracts');
|
||||
const DEFAULT_ARTIFACTS_DIR = path.resolve('artifacts');
|
||||
// Solc compiler settings cannot be configured from the commandline.
|
||||
// If you need this configured, please create a `compiler.json` config file
|
||||
// with your desired configurations.
|
||||
const DEFAULT_COMPILER_SETTINGS: solc.CompilerSettings = {
|
||||
optimizer: {
|
||||
enabled: false,
|
||||
},
|
||||
outputSelection: {
|
||||
[ALL_FILES_IDENTIFIER]: {
|
||||
[ALL_CONTRACTS_IDENTIFIER]: ['abi', 'evm.bytecode.object'],
|
||||
},
|
||||
},
|
||||
};
|
||||
const CONFIG_FILE = 'compiler.json';
|
||||
|
||||
/**
|
||||
* The Compiler facilitates compiling Solidity smart contracts and saves the results
|
||||
* to artifact files.
|
||||
*/
|
||||
export class Compiler {
|
||||
private _resolver: Resolver;
|
||||
private _nameResolver: NameResolver;
|
||||
private _contractsDir: string;
|
||||
private _compilerSettings: solc.CompilerSettings;
|
||||
private _artifactsDir: string;
|
||||
private _specifiedContracts: string[] | TYPE_ALL_FILES_IDENTIFIER;
|
||||
/**
|
||||
* Instantiates a new instance of the Compiler class.
|
||||
* @return An instance of the Compiler class.
|
||||
*/
|
||||
constructor(opts: CompilerOptions) {
|
||||
// TODO: Look for config file in parent directories if not found in current directory
|
||||
const config: CompilerOptions = fs.existsSync(CONFIG_FILE)
|
||||
? JSON.parse(fs.readFileSync(CONFIG_FILE).toString())
|
||||
: {};
|
||||
this._contractsDir = opts.contractsDir || config.contractsDir || DEFAULT_CONTRACTS_DIR;
|
||||
this._compilerSettings = opts.compilerSettings || config.compilerSettings || DEFAULT_COMPILER_SETTINGS;
|
||||
this._artifactsDir = opts.artifactsDir || config.artifactsDir || DEFAULT_ARTIFACTS_DIR;
|
||||
this._specifiedContracts = opts.contracts || config.contracts || ALL_CONTRACTS_IDENTIFIER;
|
||||
this._nameResolver = new NameResolver(path.resolve(this._contractsDir));
|
||||
const resolver = new FallthroughResolver();
|
||||
resolver.appendResolver(new URLResolver());
|
||||
const packagePath = path.resolve('');
|
||||
resolver.appendResolver(new NPMResolver(packagePath));
|
||||
resolver.appendResolver(new RelativeFSResolver(this._contractsDir));
|
||||
resolver.appendResolver(new FSResolver());
|
||||
resolver.appendResolver(this._nameResolver);
|
||||
this._resolver = resolver;
|
||||
}
|
||||
/**
|
||||
* Compiles selected Solidity files found in `contractsDir` and writes JSON artifacts to `artifactsDir`.
|
||||
*/
|
||||
public async compileAsync(): Promise<void> {
|
||||
await createDirIfDoesNotExistAsync(this._artifactsDir);
|
||||
await createDirIfDoesNotExistAsync(SOLC_BIN_DIR);
|
||||
let contractNamesToCompile: string[] = [];
|
||||
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;
|
||||
}
|
||||
for (const contractNameToCompile of contractNamesToCompile) {
|
||||
await this._compileContractAsync(contractNameToCompile);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Compiles contract and saves artifact to artifactsDir.
|
||||
* @param fileName Name of contract with '.sol' extension.
|
||||
*/
|
||||
private async _compileContractAsync(contractName: string): Promise<void> {
|
||||
const contractSource = this._resolver.resolve(contractName);
|
||||
const absoluteContractPath = path.join(this._contractsDir, contractSource.path);
|
||||
const currentArtifactIfExists = await getContractArtifactIfExistsAsync(this._artifactsDir, contractName);
|
||||
const sourceTreeHashHex = `0x${this._getSourceTreeHash(absoluteContractPath).toString('hex')}`;
|
||||
let shouldCompile = false;
|
||||
if (_.isUndefined(currentArtifactIfExists)) {
|
||||
shouldCompile = true;
|
||||
} else {
|
||||
const currentArtifact = currentArtifactIfExists as ContractArtifact;
|
||||
const isUserOnLatestVersion = currentArtifact.schemaVersion === constants.LATEST_ARTIFACT_VERSION;
|
||||
const didCompilerSettingsChange = !_.isEqual(currentArtifact.compiler.settings, this._compilerSettings);
|
||||
const didSourceChange = currentArtifact.sourceTreeHashHex !== sourceTreeHashHex;
|
||||
shouldCompile = !isUserOnLatestVersion || didCompilerSettingsChange || didSourceChange;
|
||||
}
|
||||
if (!shouldCompile) {
|
||||
return;
|
||||
}
|
||||
const solcVersionRange = parseSolidityVersionRange(contractSource.source);
|
||||
const availableCompilerVersions = _.keys(binPaths);
|
||||
const solcVersion = semver.maxSatisfying(availableCompilerVersions, solcVersionRange);
|
||||
const fullSolcVersion = binPaths[solcVersion];
|
||||
const compilerBinFilename = path.join(SOLC_BIN_DIR, fullSolcVersion);
|
||||
let solcjs: string;
|
||||
const isCompilerAvailableLocally = fs.existsSync(compilerBinFilename);
|
||||
if (isCompilerAvailableLocally) {
|
||||
solcjs = fs.readFileSync(compilerBinFilename).toString();
|
||||
} else {
|
||||
logUtils.log(`Downloading ${fullSolcVersion}...`);
|
||||
const url = `${constants.BASE_COMPILER_URL}${fullSolcVersion}`;
|
||||
const response = await fetch(url);
|
||||
if (response.status !== 200) {
|
||||
throw new Error(`Failed to load ${fullSolcVersion}`);
|
||||
}
|
||||
solcjs = await response.text();
|
||||
fs.writeFileSync(compilerBinFilename, solcjs);
|
||||
}
|
||||
const solcInstance = solc.setupMethods(requireFromString(solcjs, compilerBinFilename));
|
||||
|
||||
logUtils.log(`Compiling ${contractName} with Solidity v${solcVersion}...`);
|
||||
const source = contractSource.source;
|
||||
const standardInput: solc.StandardInput = {
|
||||
language: 'Solidity',
|
||||
sources: {
|
||||
[contractSource.path]: {
|
||||
content: contractSource.source,
|
||||
},
|
||||
},
|
||||
settings: this._compilerSettings,
|
||||
};
|
||||
const compiled: solc.StandardOutput = JSON.parse(
|
||||
solcInstance.compileStandardWrapper(JSON.stringify(standardInput), importPath => {
|
||||
const sourceCodeIfExists = this._resolver.resolve(importPath);
|
||||
return { contents: sourceCodeIfExists.source };
|
||||
}),
|
||||
);
|
||||
|
||||
if (!_.isUndefined(compiled.errors)) {
|
||||
const SOLIDITY_WARNING = 'warning';
|
||||
const errors = _.filter(compiled.errors, entry => entry.severity !== SOLIDITY_WARNING);
|
||||
const warnings = _.filter(compiled.errors, entry => entry.severity === SOLIDITY_WARNING);
|
||||
if (!_.isEmpty(errors)) {
|
||||
errors.forEach(error => {
|
||||
const normalizedErrMsg = getNormalizedErrMsg(error.formattedMessage || error.message);
|
||||
logUtils.log(chalk.red(normalizedErrMsg));
|
||||
});
|
||||
process.exit(1);
|
||||
} else {
|
||||
warnings.forEach(warning => {
|
||||
const normalizedWarningMsg = getNormalizedErrMsg(warning.formattedMessage || warning.message);
|
||||
logUtils.log(chalk.yellow(normalizedWarningMsg));
|
||||
});
|
||||
}
|
||||
}
|
||||
const compiledData = compiled.contracts[contractSource.path][contractName];
|
||||
if (_.isUndefined(compiledData)) {
|
||||
throw new Error(
|
||||
`Contract ${contractName} not found in ${
|
||||
contractSource.path
|
||||
}. 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 sourceCodes = _.mapValues(
|
||||
compiled.sources,
|
||||
(_1, sourceFilePath) => this._resolver.resolve(sourceFilePath).source,
|
||||
);
|
||||
const contractVersion: ContractVersionData = {
|
||||
compilerOutput: compiledData,
|
||||
sources: compiled.sources,
|
||||
sourceCodes,
|
||||
sourceTreeHashHex,
|
||||
compiler: {
|
||||
name: 'solc',
|
||||
version: solcVersion,
|
||||
settings: this._compilerSettings,
|
||||
},
|
||||
};
|
||||
|
||||
let newArtifact: ContractArtifact;
|
||||
if (!_.isUndefined(currentArtifactIfExists)) {
|
||||
const currentArtifact = currentArtifactIfExists as ContractArtifact;
|
||||
newArtifact = {
|
||||
...currentArtifact,
|
||||
...contractVersion,
|
||||
};
|
||||
} else {
|
||||
newArtifact = {
|
||||
schemaVersion: constants.LATEST_ARTIFACT_VERSION,
|
||||
contractName,
|
||||
...contractVersion,
|
||||
networks: {},
|
||||
};
|
||||
}
|
||||
|
||||
const artifactString = utils.stringifyWithFormatting(newArtifact);
|
||||
const currentArtifactPath = `${this._artifactsDir}/${contractName}.json`;
|
||||
await fsWrapper.writeFileAsync(currentArtifactPath, artifactString);
|
||||
logUtils.log(`${contractName} artifact saved!`);
|
||||
}
|
||||
/**
|
||||
* Gets the source tree hash for a file and its dependencies.
|
||||
* @param fileName Name of contract file.
|
||||
*/
|
||||
private _getSourceTreeHash(importPath: string): Buffer {
|
||||
const contractSource = this._resolver.resolve(importPath);
|
||||
const dependencies = parseDependencies(contractSource);
|
||||
const sourceHash = ethUtil.sha3(contractSource.source);
|
||||
if (dependencies.length === 0) {
|
||||
return sourceHash;
|
||||
} else {
|
||||
const dependencySourceTreeHashes = _.map(dependencies, (dependency: string) =>
|
||||
this._getSourceTreeHash(dependency),
|
||||
);
|
||||
const sourceTreeHashesBuffer = Buffer.concat([sourceHash, ...dependencySourceTreeHashes]);
|
||||
const sourceTreeHash = ethUtil.sha3(sourceTreeHashesBuffer);
|
||||
return sourceTreeHash;
|
||||
}
|
||||
}
|
||||
}
|
||||
6
packages/sol-compiler/src/globals.d.ts
vendored
Normal file
6
packages/sol-compiler/src/globals.d.ts
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
declare module '*.json' {
|
||||
const json: any;
|
||||
/* tslint:disable */
|
||||
export default json;
|
||||
/* tslint:enable */
|
||||
}
|
||||
2
packages/sol-compiler/src/index.ts
Normal file
2
packages/sol-compiler/src/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export { Compiler } from './compiler';
|
||||
export { ContractArtifact, ContractNetworks } from './utils/types';
|
||||
@@ -0,0 +1,8 @@
|
||||
import { postpublishUtils } from '@0xproject/monorepo-scripts';
|
||||
|
||||
import * as packageJSON from '../package.json';
|
||||
import * as tsConfigJSON from '../tsconfig.json';
|
||||
|
||||
const cwd = `${__dirname}/..`;
|
||||
// tslint:disable-next-line:no-floating-promises
|
||||
postpublishUtils.runAsync(packageJSON, tsConfigJSON, cwd);
|
||||
8
packages/sol-compiler/src/monorepo_scripts/stage_docs.ts
Normal file
8
packages/sol-compiler/src/monorepo_scripts/stage_docs.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { postpublishUtils } from '@0xproject/monorepo-scripts';
|
||||
|
||||
import * as packageJSON from '../package.json';
|
||||
import * as tsConfigJSON from '../tsconfig.json';
|
||||
|
||||
const cwd = `${__dirname}/..`;
|
||||
// tslint:disable-next-line:no-floating-promises
|
||||
postpublishUtils.publishDocsToStagingAsync(packageJSON, tsConfigJSON, cwd);
|
||||
20
packages/sol-compiler/src/solc/bin_paths.ts
Normal file
20
packages/sol-compiler/src/solc/bin_paths.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
export interface BinaryPaths {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
export const binPaths: BinaryPaths = {
|
||||
'0.4.10': 'soljson-v0.4.10+commit.f0d539ae.js',
|
||||
'0.4.11': 'soljson-v0.4.11+commit.68ef5810.js',
|
||||
'0.4.12': 'soljson-v0.4.12+commit.194ff033.js',
|
||||
'0.4.13': 'soljson-v0.4.13+commit.fb4cb1a.js',
|
||||
'0.4.14': 'soljson-v0.4.14+commit.c2215d46.js',
|
||||
'0.4.15': 'soljson-v0.4.15+commit.bbb8e64f.js',
|
||||
'0.4.16': 'soljson-v0.4.16+commit.d7661dd9.js',
|
||||
'0.4.17': 'soljson-v0.4.17+commit.bdeb9e52.js',
|
||||
'0.4.18': 'soljson-v0.4.18+commit.9cf6e910.js',
|
||||
'0.4.19': 'soljson-v0.4.19+commit.c4cbbb05.js',
|
||||
'0.4.20': 'soljson-v0.4.20+commit.3155dd80.js',
|
||||
'0.4.21': 'soljson-v0.4.21+commit.dfe3193c.js',
|
||||
'0.4.22': 'soljson-v0.4.22+commit.4cb486ee.js',
|
||||
'0.4.23': 'soljson-v0.4.23+commit.124ca40d.js',
|
||||
};
|
||||
107
packages/sol-compiler/src/utils/compiler.ts
Normal file
107
packages/sol-compiler/src/utils/compiler.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
import { ContractSource, ContractSources } from '@0xproject/sol-resolver';
|
||||
import { logUtils } from '@0xproject/utils';
|
||||
import * as _ from 'lodash';
|
||||
import * as path from 'path';
|
||||
import * as solc from 'solc';
|
||||
|
||||
import { constants } from './constants';
|
||||
import { fsWrapper } from './fs_wrapper';
|
||||
import { ContractArtifact } from './types';
|
||||
|
||||
/**
|
||||
* Gets contract data on network or returns if an artifact does not exist.
|
||||
* @param artifactsDir Path to the artifacts directory.
|
||||
* @param contractName Name of contract.
|
||||
* @return Contract data on network or undefined.
|
||||
*/
|
||||
export async function getContractArtifactIfExistsAsync(
|
||||
artifactsDir: string,
|
||||
contractName: string,
|
||||
): Promise<ContractArtifact | void> {
|
||||
let contractArtifact;
|
||||
const currentArtifactPath = `${artifactsDir}/${contractName}.json`;
|
||||
try {
|
||||
const opts = {
|
||||
encoding: 'utf8',
|
||||
};
|
||||
const contractArtifactString = await fsWrapper.readFileAsync(currentArtifactPath, opts);
|
||||
contractArtifact = JSON.parse(contractArtifactString);
|
||||
return contractArtifact;
|
||||
} catch (err) {
|
||||
logUtils.log(`Artifact for ${contractName} does not exist`);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a directory if it does not already exist.
|
||||
* @param artifactsDir Path to the directory.
|
||||
*/
|
||||
export async function createDirIfDoesNotExistAsync(dirPath: string): Promise<void> {
|
||||
if (!fsWrapper.doesPathExistSync(dirPath)) {
|
||||
logUtils.log(`Creating directory at ${dirPath}...`);
|
||||
await fsWrapper.mkdirpAsync(dirPath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches Solidity source code for compiler version range.
|
||||
* @param source Source code of contract.
|
||||
* @return Solc compiler version range.
|
||||
*/
|
||||
export function parseSolidityVersionRange(source: string): string {
|
||||
const SOLIDITY_VERSION_RANGE_REGEX = /pragma\s+solidity\s+(.*);/;
|
||||
const solcVersionRangeMatch = source.match(SOLIDITY_VERSION_RANGE_REGEX);
|
||||
if (_.isNull(solcVersionRangeMatch)) {
|
||||
throw new Error('Could not find Solidity version range in source');
|
||||
}
|
||||
const solcVersionRange = solcVersionRangeMatch[1];
|
||||
return solcVersionRange;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes the path found in the error message.
|
||||
* Example: converts 'base/Token.sol:6:46: Warning: Unused local variable'
|
||||
* to 'Token.sol:6:46: Warning: Unused local variable'
|
||||
* This is used to prevent logging the same error multiple times.
|
||||
* @param errMsg An error message from the compiled output.
|
||||
* @return The error message with directories truncated from the contract path.
|
||||
*/
|
||||
export function getNormalizedErrMsg(errMsg: string): string {
|
||||
const SOLIDITY_FILE_EXTENSION_REGEX = /(.*\.sol)/;
|
||||
const errPathMatch = errMsg.match(SOLIDITY_FILE_EXTENSION_REGEX);
|
||||
if (_.isNull(errPathMatch)) {
|
||||
throw new Error('Could not find a path in error message');
|
||||
}
|
||||
const errPath = errPathMatch[0];
|
||||
const baseContract = path.basename(errPath);
|
||||
const normalizedErrMsg = errMsg.replace(errPath, baseContract);
|
||||
return normalizedErrMsg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the contract source code and extracts the dendencies
|
||||
* @param source Contract source code
|
||||
* @return List of dependendencies
|
||||
*/
|
||||
export function parseDependencies(contractSource: ContractSource): string[] {
|
||||
// TODO: Use a proper parser
|
||||
const source = contractSource.source;
|
||||
const IMPORT_REGEX = /(import\s)/;
|
||||
const DEPENDENCY_PATH_REGEX = /"([^"]+)"/; // Source: https://github.com/BlockChainCompany/soljitsu/blob/master/lib/shared.js
|
||||
const dependencies: string[] = [];
|
||||
const lines = source.split('\n');
|
||||
_.forEach(lines, line => {
|
||||
if (!_.isNull(line.match(IMPORT_REGEX))) {
|
||||
const dependencyMatch = line.match(DEPENDENCY_PATH_REGEX);
|
||||
if (!_.isNull(dependencyMatch)) {
|
||||
let dependencyPath = dependencyMatch[1];
|
||||
if (dependencyPath.startsWith('.')) {
|
||||
dependencyPath = path.join(path.dirname(contractSource.path), dependencyPath);
|
||||
}
|
||||
dependencies.push(dependencyPath);
|
||||
}
|
||||
}
|
||||
});
|
||||
return dependencies;
|
||||
}
|
||||
5
packages/sol-compiler/src/utils/constants.ts
Normal file
5
packages/sol-compiler/src/utils/constants.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export const constants = {
|
||||
SOLIDITY_FILE_EXTENSION: '.sol',
|
||||
BASE_COMPILER_URL: 'https://ethereum.github.io/solc-bin/bin/',
|
||||
LATEST_ARTIFACT_VERSION: '2.0.0',
|
||||
};
|
||||
18
packages/sol-compiler/src/utils/encoder.ts
Normal file
18
packages/sol-compiler/src/utils/encoder.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { AbiDefinition, AbiType, ContractAbi, DataItem } from '@0xproject/types';
|
||||
import * as _ from 'lodash';
|
||||
import * as web3Abi from 'web3-eth-abi';
|
||||
|
||||
export const encoder = {
|
||||
encodeConstructorArgsFromAbi(args: any[], abi: ContractAbi): string {
|
||||
const constructorTypes: string[] = [];
|
||||
_.each(abi, (element: AbiDefinition) => {
|
||||
if (element.type === AbiType.Constructor) {
|
||||
_.each(element.inputs, (input: DataItem) => {
|
||||
constructorTypes.push(input.type);
|
||||
});
|
||||
}
|
||||
});
|
||||
const encodedParameters = web3Abi.encodeParameters(constructorTypes, args);
|
||||
return encodedParameters;
|
||||
},
|
||||
};
|
||||
13
packages/sol-compiler/src/utils/fs_wrapper.ts
Normal file
13
packages/sol-compiler/src/utils/fs_wrapper.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { promisify } from '@0xproject/utils';
|
||||
import * as fs from 'fs';
|
||||
import * as mkdirp from 'mkdirp';
|
||||
|
||||
export const fsWrapper = {
|
||||
readdirAsync: promisify<string[]>(fs.readdir),
|
||||
readFileAsync: promisify<string>(fs.readFile),
|
||||
writeFileAsync: promisify<undefined>(fs.writeFile),
|
||||
mkdirpAsync: promisify<undefined>(mkdirp),
|
||||
doesPathExistSync: fs.existsSync,
|
||||
rmdirSync: fs.rmdirSync,
|
||||
removeFileAsync: promisify<undefined>(fs.unlink),
|
||||
};
|
||||
79
packages/sol-compiler/src/utils/types.ts
Normal file
79
packages/sol-compiler/src/utils/types.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import { ContractAbi, Provider, TxData } from '@0xproject/types';
|
||||
import * as solc from 'solc';
|
||||
import * as Web3 from 'web3';
|
||||
import * as yargs from 'yargs';
|
||||
|
||||
export enum AbiType {
|
||||
Function = 'function',
|
||||
Constructor = 'constructor',
|
||||
Event = 'event',
|
||||
Fallback = 'fallback',
|
||||
}
|
||||
|
||||
export interface ContractArtifact extends ContractVersionData {
|
||||
schemaVersion: string;
|
||||
contractName: string;
|
||||
networks: ContractNetworks;
|
||||
}
|
||||
|
||||
export interface ContractVersionData {
|
||||
compiler: {
|
||||
name: 'solc';
|
||||
version: string;
|
||||
settings: solc.CompilerSettings;
|
||||
};
|
||||
sources: {
|
||||
[sourceName: string]: {
|
||||
id: number;
|
||||
};
|
||||
};
|
||||
sourceCodes: {
|
||||
[sourceName: string]: string;
|
||||
};
|
||||
sourceTreeHashHex: string;
|
||||
compilerOutput: solc.StandardContractOutput;
|
||||
}
|
||||
|
||||
export interface ContractNetworks {
|
||||
[networkId: number]: ContractNetworkData;
|
||||
}
|
||||
|
||||
export interface ContractNetworkData {
|
||||
address: string;
|
||||
links: {
|
||||
[linkName: string]: string;
|
||||
};
|
||||
constructorArgs: string;
|
||||
}
|
||||
|
||||
export interface SolcErrors {
|
||||
[key: string]: boolean;
|
||||
}
|
||||
|
||||
export interface CompilerOptions {
|
||||
contractsDir?: string;
|
||||
artifactsDir?: string;
|
||||
compilerSettings?: solc.CompilerSettings;
|
||||
contracts?: string[] | '*';
|
||||
}
|
||||
|
||||
export interface ContractSourceData {
|
||||
[contractName: string]: ContractSpecificSourceData;
|
||||
}
|
||||
|
||||
export interface ContractSpecificSourceData {
|
||||
solcVersionRange: string;
|
||||
sourceHash: Buffer;
|
||||
sourceTreeHash: Buffer;
|
||||
}
|
||||
|
||||
export interface Token {
|
||||
address?: string;
|
||||
name: string;
|
||||
symbol: string;
|
||||
decimals: number;
|
||||
ipfsHash: string;
|
||||
swarmHash: string;
|
||||
}
|
||||
|
||||
export type DoneCallback = (err?: Error) => void;
|
||||
6
packages/sol-compiler/src/utils/utils.ts
Normal file
6
packages/sol-compiler/src/utils/utils.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export const utils = {
|
||||
stringifyWithFormatting(obj: any): string {
|
||||
const stringifiedObj = JSON.stringify(obj, null, '\t');
|
||||
return stringifiedObj;
|
||||
},
|
||||
};
|
||||
45
packages/sol-compiler/test/compiler_test.ts
Normal file
45
packages/sol-compiler/test/compiler_test.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { DoneCallback } from '@0xproject/types';
|
||||
import * as chai from 'chai';
|
||||
import 'mocha';
|
||||
|
||||
import { Compiler } from '../src/compiler';
|
||||
import { fsWrapper } from '../src/utils/fs_wrapper';
|
||||
import { CompilerOptions, ContractArtifact, ContractNetworkData } from '../src/utils/types';
|
||||
|
||||
import { exchange_binary } from './fixtures/exchange_bin';
|
||||
import { constants } from './util/constants';
|
||||
|
||||
const expect = chai.expect;
|
||||
|
||||
describe('#Compiler', function() {
|
||||
this.timeout(constants.timeoutMs);
|
||||
const artifactsDir = `${__dirname}/fixtures/artifacts`;
|
||||
const contractsDir = `${__dirname}/fixtures/contracts`;
|
||||
const exchangeArtifactPath = `${artifactsDir}/Exchange.json`;
|
||||
const compilerOpts: CompilerOptions = {
|
||||
artifactsDir,
|
||||
contractsDir,
|
||||
contracts: constants.contracts,
|
||||
};
|
||||
const compiler = new Compiler(compilerOpts);
|
||||
beforeEach((done: DoneCallback) => {
|
||||
(async () => {
|
||||
if (fsWrapper.doesPathExistSync(exchangeArtifactPath)) {
|
||||
await fsWrapper.removeFileAsync(exchangeArtifactPath);
|
||||
}
|
||||
await compiler.compileAsync();
|
||||
done();
|
||||
})().catch(done);
|
||||
});
|
||||
it('should create an Exchange artifact with the correct unlinked binary', async () => {
|
||||
const opts = {
|
||||
encoding: 'utf8',
|
||||
};
|
||||
const exchangeArtifactString = await fsWrapper.readFileAsync(exchangeArtifactPath, opts);
|
||||
const exchangeArtifact: ContractArtifact = JSON.parse(exchangeArtifactString);
|
||||
// The last 43 bytes of the binaries are metadata which may not be equivalent
|
||||
const unlinkedBinaryWithoutMetadata = exchangeArtifact.compilerOutput.evm.bytecode.object.slice(2, -86);
|
||||
const exchangeBinaryWithoutMetadata = exchange_binary.slice(0, -86);
|
||||
expect(unlinkedBinaryWithoutMetadata).to.equal(exchangeBinaryWithoutMetadata);
|
||||
});
|
||||
});
|
||||
83
packages/sol-compiler/test/compiler_utils_test.ts
Normal file
83
packages/sol-compiler/test/compiler_utils_test.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import * as chai from 'chai';
|
||||
import * as dirtyChai from 'dirty-chai';
|
||||
import * as _ from 'lodash';
|
||||
import 'mocha';
|
||||
|
||||
import {
|
||||
createDirIfDoesNotExistAsync,
|
||||
getNormalizedErrMsg,
|
||||
parseDependencies,
|
||||
parseSolidityVersionRange,
|
||||
} from '../src/utils/compiler';
|
||||
import { fsWrapper } from '../src/utils/fs_wrapper';
|
||||
|
||||
chai.use(dirtyChai);
|
||||
const expect = chai.expect;
|
||||
|
||||
describe('Compiler utils', () => {
|
||||
describe('#getNormalizedErrorMessage', () => {
|
||||
it('normalizes the error message', () => {
|
||||
const errMsg = 'base/Token.sol:6:46: Warning: Unused local variable';
|
||||
const normalizedErrMsg = getNormalizedErrMsg(errMsg);
|
||||
expect(normalizedErrMsg).to.be.equal('Token.sol:6:46: Warning: Unused local variable');
|
||||
});
|
||||
});
|
||||
describe('#createDirIfDoesNotExistAsync', () => {
|
||||
it('creates artifacts dir', async () => {
|
||||
const artifactsDir = `${__dirname}/artifacts`;
|
||||
expect(fsWrapper.doesPathExistSync(artifactsDir)).to.be.false();
|
||||
await createDirIfDoesNotExistAsync(artifactsDir);
|
||||
expect(fsWrapper.doesPathExistSync(artifactsDir)).to.be.true();
|
||||
fsWrapper.rmdirSync(artifactsDir);
|
||||
expect(fsWrapper.doesPathExistSync(artifactsDir)).to.be.false();
|
||||
});
|
||||
});
|
||||
describe('#parseSolidityVersionRange', () => {
|
||||
it('correctly parses the version range', () => {
|
||||
expect(parseSolidityVersionRange('pragma solidity ^0.0.1;')).to.be.equal('^0.0.1');
|
||||
expect(parseSolidityVersionRange('\npragma solidity 0.0.1;')).to.be.equal('0.0.1');
|
||||
expect(parseSolidityVersionRange('pragma solidity <=1.0.1;')).to.be.equal('<=1.0.1');
|
||||
expect(parseSolidityVersionRange('pragma solidity ~1.0.1;')).to.be.equal('~1.0.1');
|
||||
});
|
||||
// TODO: For now that doesn't work. This will work after we switch to a grammar-based parser
|
||||
it.skip('correctly parses the version range with comments', () => {
|
||||
expect(parseSolidityVersionRange('// pragma solidity ~1.0.1;\npragma solidity ~1.0.2;')).to.be.equal(
|
||||
'~1.0.2',
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('#parseDependencies', () => {
|
||||
it('correctly parses Exchange dependencies', async () => {
|
||||
const path = `${__dirname}/fixtures/contracts/Exchange.sol`;
|
||||
const source = await fsWrapper.readFileAsync(path, {
|
||||
encoding: 'utf8',
|
||||
});
|
||||
const dependencies = parseDependencies({ source, path });
|
||||
const expectedDependencies = [
|
||||
'zeppelin-solidity/contracts/token/ERC20/ERC20.sol',
|
||||
'packages/sol-compiler/lib/test/fixtures/contracts/TokenTransferProxy.sol',
|
||||
'packages/sol-compiler/lib/test/fixtures/contracts/base/SafeMath.sol',
|
||||
];
|
||||
_.each(expectedDependencies, expectedDepdency => {
|
||||
const foundDependency = _.find(dependencies, dependency => _.endsWith(dependency, expectedDepdency));
|
||||
expect(foundDependency, `${expectedDepdency} not found`).to.not.be.undefined();
|
||||
});
|
||||
});
|
||||
it('correctly parses TokenTransferProxy dependencies', async () => {
|
||||
const path = `${__dirname}/fixtures/contracts/TokenTransferProxy.sol`;
|
||||
const source = await fsWrapper.readFileAsync(path, {
|
||||
encoding: 'utf8',
|
||||
});
|
||||
expect(parseDependencies({ source, path })).to.be.deep.equal([
|
||||
'zeppelin-solidity/contracts/ownership/Ownable.sol',
|
||||
'zeppelin-solidity/contracts/token/ERC20/ERC20.sol',
|
||||
]);
|
||||
});
|
||||
// TODO: For now that doesn't work. This will work after we switch to a grammar-based parser
|
||||
it.skip('correctly parses commented out dependencies', async () => {
|
||||
const path = '';
|
||||
const source = `// import "./TokenTransferProxy.sol";`;
|
||||
expect(parseDependencies({ path, source })).to.be.deep.equal([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
603
packages/sol-compiler/test/fixtures/contracts/Exchange.sol
vendored
Normal file
603
packages/sol-compiler/test/fixtures/contracts/Exchange.sol
vendored
Normal file
@@ -0,0 +1,603 @@
|
||||
/*
|
||||
|
||||
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 {ERC20 as Token} from "zeppelin-solidity/contracts/token/ERC20/ERC20.sol";
|
||||
|
||||
import "./TokenTransferProxy.sol";
|
||||
import "./base/SafeMath.sol";
|
||||
|
||||
/// @title Exchange - Facilitates exchange of ERC20 tokens.
|
||||
/// @author Amir Bandeali - <amir@0xProject.com>, Will Warren - <will@0xProject.com>
|
||||
contract Exchange is SafeMath {
|
||||
|
||||
// Error Codes
|
||||
enum Errors {
|
||||
ORDER_EXPIRED, // Order has already expired
|
||||
ORDER_FULLY_FILLED_OR_CANCELLED, // Order has already been fully filled or cancelled
|
||||
ROUNDING_ERROR_TOO_LARGE, // Rounding error too large
|
||||
INSUFFICIENT_BALANCE_OR_ALLOWANCE // Insufficient balance or allowance for token transfer
|
||||
}
|
||||
|
||||
string constant public VERSION = "1.0.0";
|
||||
uint16 constant public EXTERNAL_QUERY_GAS_LIMIT = 4999; // Changes to state require at least 5000 gas
|
||||
|
||||
address public ZRX_TOKEN_CONTRACT;
|
||||
address public TOKEN_TRANSFER_PROXY_CONTRACT;
|
||||
|
||||
// Mappings of orderHash => amounts of takerTokenAmount filled or cancelled.
|
||||
mapping (bytes32 => uint) public filled;
|
||||
mapping (bytes32 => uint) public cancelled;
|
||||
|
||||
event LogFill(
|
||||
address indexed maker,
|
||||
address taker,
|
||||
address indexed feeRecipient,
|
||||
address makerToken,
|
||||
address takerToken,
|
||||
uint filledMakerTokenAmount,
|
||||
uint filledTakerTokenAmount,
|
||||
uint paidMakerFee,
|
||||
uint paidTakerFee,
|
||||
bytes32 indexed tokens, // keccak256(makerToken, takerToken), allows subscribing to a token pair
|
||||
bytes32 orderHash
|
||||
);
|
||||
|
||||
event LogCancel(
|
||||
address indexed maker,
|
||||
address indexed feeRecipient,
|
||||
address makerToken,
|
||||
address takerToken,
|
||||
uint cancelledMakerTokenAmount,
|
||||
uint cancelledTakerTokenAmount,
|
||||
bytes32 indexed tokens,
|
||||
bytes32 orderHash
|
||||
);
|
||||
|
||||
event LogError(uint8 indexed errorId, bytes32 indexed orderHash);
|
||||
|
||||
struct Order {
|
||||
address maker;
|
||||
address taker;
|
||||
address makerToken;
|
||||
address takerToken;
|
||||
address feeRecipient;
|
||||
uint makerTokenAmount;
|
||||
uint takerTokenAmount;
|
||||
uint makerFee;
|
||||
uint takerFee;
|
||||
uint expirationTimestampInSec;
|
||||
bytes32 orderHash;
|
||||
}
|
||||
|
||||
function Exchange(address _zrxToken, address _tokenTransferProxy) {
|
||||
ZRX_TOKEN_CONTRACT = _zrxToken;
|
||||
TOKEN_TRANSFER_PROXY_CONTRACT = _tokenTransferProxy;
|
||||
}
|
||||
|
||||
/*
|
||||
* Core exchange functions
|
||||
*/
|
||||
|
||||
/// @dev Fills the input order.
|
||||
/// @param orderAddresses Array of order's maker, taker, makerToken, takerToken, and feeRecipient.
|
||||
/// @param orderValues Array of order's makerTokenAmount, takerTokenAmount, makerFee, takerFee, expirationTimestampInSec, and salt.
|
||||
/// @param fillTakerTokenAmount Desired amount of takerToken to fill.
|
||||
/// @param shouldThrowOnInsufficientBalanceOrAllowance Test if transfer will fail before attempting.
|
||||
/// @param v ECDSA signature parameter v.
|
||||
/// @param r ECDSA signature parameters r.
|
||||
/// @param s ECDSA signature parameters s.
|
||||
/// @return Total amount of takerToken filled in trade.
|
||||
function fillOrder(
|
||||
address[5] orderAddresses,
|
||||
uint[6] orderValues,
|
||||
uint fillTakerTokenAmount,
|
||||
bool shouldThrowOnInsufficientBalanceOrAllowance,
|
||||
uint8 v,
|
||||
bytes32 r,
|
||||
bytes32 s)
|
||||
public
|
||||
returns (uint filledTakerTokenAmount)
|
||||
{
|
||||
Order memory order = Order({
|
||||
maker: orderAddresses[0],
|
||||
taker: orderAddresses[1],
|
||||
makerToken: orderAddresses[2],
|
||||
takerToken: orderAddresses[3],
|
||||
feeRecipient: orderAddresses[4],
|
||||
makerTokenAmount: orderValues[0],
|
||||
takerTokenAmount: orderValues[1],
|
||||
makerFee: orderValues[2],
|
||||
takerFee: orderValues[3],
|
||||
expirationTimestampInSec: orderValues[4],
|
||||
orderHash: getOrderHash(orderAddresses, orderValues)
|
||||
});
|
||||
|
||||
require(order.taker == address(0) || order.taker == msg.sender);
|
||||
require(order.makerTokenAmount > 0 && order.takerTokenAmount > 0 && fillTakerTokenAmount > 0);
|
||||
require(isValidSignature(
|
||||
order.maker,
|
||||
order.orderHash,
|
||||
v,
|
||||
r,
|
||||
s
|
||||
));
|
||||
|
||||
if (block.timestamp >= order.expirationTimestampInSec) {
|
||||
LogError(uint8(Errors.ORDER_EXPIRED), order.orderHash);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint remainingTakerTokenAmount = safeSub(order.takerTokenAmount, getUnavailableTakerTokenAmount(order.orderHash));
|
||||
filledTakerTokenAmount = min256(fillTakerTokenAmount, remainingTakerTokenAmount);
|
||||
if (filledTakerTokenAmount == 0) {
|
||||
LogError(uint8(Errors.ORDER_FULLY_FILLED_OR_CANCELLED), order.orderHash);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (isRoundingError(filledTakerTokenAmount, order.takerTokenAmount, order.makerTokenAmount)) {
|
||||
LogError(uint8(Errors.ROUNDING_ERROR_TOO_LARGE), order.orderHash);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!shouldThrowOnInsufficientBalanceOrAllowance && !isTransferable(order, filledTakerTokenAmount)) {
|
||||
LogError(uint8(Errors.INSUFFICIENT_BALANCE_OR_ALLOWANCE), order.orderHash);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint filledMakerTokenAmount = getPartialAmount(filledTakerTokenAmount, order.takerTokenAmount, order.makerTokenAmount);
|
||||
uint paidMakerFee;
|
||||
uint paidTakerFee;
|
||||
filled[order.orderHash] = safeAdd(filled[order.orderHash], filledTakerTokenAmount);
|
||||
require(transferViaTokenTransferProxy(
|
||||
order.makerToken,
|
||||
order.maker,
|
||||
msg.sender,
|
||||
filledMakerTokenAmount
|
||||
));
|
||||
require(transferViaTokenTransferProxy(
|
||||
order.takerToken,
|
||||
msg.sender,
|
||||
order.maker,
|
||||
filledTakerTokenAmount
|
||||
));
|
||||
if (order.feeRecipient != address(0)) {
|
||||
if (order.makerFee > 0) {
|
||||
paidMakerFee = getPartialAmount(filledTakerTokenAmount, order.takerTokenAmount, order.makerFee);
|
||||
require(transferViaTokenTransferProxy(
|
||||
ZRX_TOKEN_CONTRACT,
|
||||
order.maker,
|
||||
order.feeRecipient,
|
||||
paidMakerFee
|
||||
));
|
||||
}
|
||||
if (order.takerFee > 0) {
|
||||
paidTakerFee = getPartialAmount(filledTakerTokenAmount, order.takerTokenAmount, order.takerFee);
|
||||
require(transferViaTokenTransferProxy(
|
||||
ZRX_TOKEN_CONTRACT,
|
||||
msg.sender,
|
||||
order.feeRecipient,
|
||||
paidTakerFee
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
LogFill(
|
||||
order.maker,
|
||||
msg.sender,
|
||||
order.feeRecipient,
|
||||
order.makerToken,
|
||||
order.takerToken,
|
||||
filledMakerTokenAmount,
|
||||
filledTakerTokenAmount,
|
||||
paidMakerFee,
|
||||
paidTakerFee,
|
||||
keccak256(order.makerToken, order.takerToken),
|
||||
order.orderHash
|
||||
);
|
||||
return filledTakerTokenAmount;
|
||||
}
|
||||
|
||||
/// @dev Cancels the input order.
|
||||
/// @param orderAddresses Array of order's maker, taker, makerToken, takerToken, and feeRecipient.
|
||||
/// @param orderValues Array of order's makerTokenAmount, takerTokenAmount, makerFee, takerFee, expirationTimestampInSec, and salt.
|
||||
/// @param cancelTakerTokenAmount Desired amount of takerToken to cancel in order.
|
||||
/// @return Amount of takerToken cancelled.
|
||||
function cancelOrder(
|
||||
address[5] orderAddresses,
|
||||
uint[6] orderValues,
|
||||
uint cancelTakerTokenAmount)
|
||||
public
|
||||
returns (uint)
|
||||
{
|
||||
Order memory order = Order({
|
||||
maker: orderAddresses[0],
|
||||
taker: orderAddresses[1],
|
||||
makerToken: orderAddresses[2],
|
||||
takerToken: orderAddresses[3],
|
||||
feeRecipient: orderAddresses[4],
|
||||
makerTokenAmount: orderValues[0],
|
||||
takerTokenAmount: orderValues[1],
|
||||
makerFee: orderValues[2],
|
||||
takerFee: orderValues[3],
|
||||
expirationTimestampInSec: orderValues[4],
|
||||
orderHash: getOrderHash(orderAddresses, orderValues)
|
||||
});
|
||||
|
||||
require(order.maker == msg.sender);
|
||||
require(order.makerTokenAmount > 0 && order.takerTokenAmount > 0 && cancelTakerTokenAmount > 0);
|
||||
|
||||
if (block.timestamp >= order.expirationTimestampInSec) {
|
||||
LogError(uint8(Errors.ORDER_EXPIRED), order.orderHash);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint remainingTakerTokenAmount = safeSub(order.takerTokenAmount, getUnavailableTakerTokenAmount(order.orderHash));
|
||||
uint cancelledTakerTokenAmount = min256(cancelTakerTokenAmount, remainingTakerTokenAmount);
|
||||
if (cancelledTakerTokenAmount == 0) {
|
||||
LogError(uint8(Errors.ORDER_FULLY_FILLED_OR_CANCELLED), order.orderHash);
|
||||
return 0;
|
||||
}
|
||||
|
||||
cancelled[order.orderHash] = safeAdd(cancelled[order.orderHash], cancelledTakerTokenAmount);
|
||||
|
||||
LogCancel(
|
||||
order.maker,
|
||||
order.feeRecipient,
|
||||
order.makerToken,
|
||||
order.takerToken,
|
||||
getPartialAmount(cancelledTakerTokenAmount, order.takerTokenAmount, order.makerTokenAmount),
|
||||
cancelledTakerTokenAmount,
|
||||
keccak256(order.makerToken, order.takerToken),
|
||||
order.orderHash
|
||||
);
|
||||
return cancelledTakerTokenAmount;
|
||||
}
|
||||
|
||||
/*
|
||||
* Wrapper functions
|
||||
*/
|
||||
|
||||
/// @dev Fills an order with specified parameters and ECDSA signature, throws if specified amount not filled entirely.
|
||||
/// @param orderAddresses Array of order's maker, taker, makerToken, takerToken, and feeRecipient.
|
||||
/// @param orderValues Array of order's makerTokenAmount, takerTokenAmount, makerFee, takerFee, expirationTimestampInSec, and salt.
|
||||
/// @param fillTakerTokenAmount Desired amount of takerToken to fill.
|
||||
/// @param v ECDSA signature parameter v.
|
||||
/// @param r ECDSA signature parameters r.
|
||||
/// @param s ECDSA signature parameters s.
|
||||
function fillOrKillOrder(
|
||||
address[5] orderAddresses,
|
||||
uint[6] orderValues,
|
||||
uint fillTakerTokenAmount,
|
||||
uint8 v,
|
||||
bytes32 r,
|
||||
bytes32 s)
|
||||
public
|
||||
{
|
||||
require(fillOrder(
|
||||
orderAddresses,
|
||||
orderValues,
|
||||
fillTakerTokenAmount,
|
||||
false,
|
||||
v,
|
||||
r,
|
||||
s
|
||||
) == fillTakerTokenAmount);
|
||||
}
|
||||
|
||||
/// @dev Synchronously executes multiple fill orders in a single transaction.
|
||||
/// @param orderAddresses Array of address arrays containing individual order addresses.
|
||||
/// @param orderValues Array of uint arrays containing individual order values.
|
||||
/// @param fillTakerTokenAmounts Array of desired amounts of takerToken to fill in orders.
|
||||
/// @param shouldThrowOnInsufficientBalanceOrAllowance Test if transfers will fail before attempting.
|
||||
/// @param v Array ECDSA signature v parameters.
|
||||
/// @param r Array of ECDSA signature r parameters.
|
||||
/// @param s Array of ECDSA signature s parameters.
|
||||
function batchFillOrders(
|
||||
address[5][] orderAddresses,
|
||||
uint[6][] orderValues,
|
||||
uint[] fillTakerTokenAmounts,
|
||||
bool shouldThrowOnInsufficientBalanceOrAllowance,
|
||||
uint8[] v,
|
||||
bytes32[] r,
|
||||
bytes32[] s)
|
||||
public
|
||||
{
|
||||
for (uint i = 0; i < orderAddresses.length; i++) {
|
||||
fillOrder(
|
||||
orderAddresses[i],
|
||||
orderValues[i],
|
||||
fillTakerTokenAmounts[i],
|
||||
shouldThrowOnInsufficientBalanceOrAllowance,
|
||||
v[i],
|
||||
r[i],
|
||||
s[i]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Synchronously executes multiple fillOrKill orders in a single transaction.
|
||||
/// @param orderAddresses Array of address arrays containing individual order addresses.
|
||||
/// @param orderValues Array of uint arrays containing individual order values.
|
||||
/// @param fillTakerTokenAmounts Array of desired amounts of takerToken to fill in orders.
|
||||
/// @param v Array ECDSA signature v parameters.
|
||||
/// @param r Array of ECDSA signature r parameters.
|
||||
/// @param s Array of ECDSA signature s parameters.
|
||||
function batchFillOrKillOrders(
|
||||
address[5][] orderAddresses,
|
||||
uint[6][] orderValues,
|
||||
uint[] fillTakerTokenAmounts,
|
||||
uint8[] v,
|
||||
bytes32[] r,
|
||||
bytes32[] s)
|
||||
public
|
||||
{
|
||||
for (uint i = 0; i < orderAddresses.length; i++) {
|
||||
fillOrKillOrder(
|
||||
orderAddresses[i],
|
||||
orderValues[i],
|
||||
fillTakerTokenAmounts[i],
|
||||
v[i],
|
||||
r[i],
|
||||
s[i]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Synchronously executes multiple fill orders in a single transaction until total fillTakerTokenAmount filled.
|
||||
/// @param orderAddresses Array of address arrays containing individual order addresses.
|
||||
/// @param orderValues Array of uint arrays containing individual order values.
|
||||
/// @param fillTakerTokenAmount Desired total amount of takerToken to fill in orders.
|
||||
/// @param shouldThrowOnInsufficientBalanceOrAllowance Test if transfers will fail before attempting.
|
||||
/// @param v Array ECDSA signature v parameters.
|
||||
/// @param r Array of ECDSA signature r parameters.
|
||||
/// @param s Array of ECDSA signature s parameters.
|
||||
/// @return Total amount of fillTakerTokenAmount filled in orders.
|
||||
function fillOrdersUpTo(
|
||||
address[5][] orderAddresses,
|
||||
uint[6][] orderValues,
|
||||
uint fillTakerTokenAmount,
|
||||
bool shouldThrowOnInsufficientBalanceOrAllowance,
|
||||
uint8[] v,
|
||||
bytes32[] r,
|
||||
bytes32[] s)
|
||||
public
|
||||
returns (uint)
|
||||
{
|
||||
uint filledTakerTokenAmount = 0;
|
||||
for (uint i = 0; i < orderAddresses.length; i++) {
|
||||
require(orderAddresses[i][3] == orderAddresses[0][3]); // takerToken must be the same for each order
|
||||
filledTakerTokenAmount = safeAdd(filledTakerTokenAmount, fillOrder(
|
||||
orderAddresses[i],
|
||||
orderValues[i],
|
||||
safeSub(fillTakerTokenAmount, filledTakerTokenAmount),
|
||||
shouldThrowOnInsufficientBalanceOrAllowance,
|
||||
v[i],
|
||||
r[i],
|
||||
s[i]
|
||||
));
|
||||
if (filledTakerTokenAmount == fillTakerTokenAmount) break;
|
||||
}
|
||||
return filledTakerTokenAmount;
|
||||
}
|
||||
|
||||
/// @dev Synchronously cancels multiple orders in a single transaction.
|
||||
/// @param orderAddresses Array of address arrays containing individual order addresses.
|
||||
/// @param orderValues Array of uint arrays containing individual order values.
|
||||
/// @param cancelTakerTokenAmounts Array of desired amounts of takerToken to cancel in orders.
|
||||
function batchCancelOrders(
|
||||
address[5][] orderAddresses,
|
||||
uint[6][] orderValues,
|
||||
uint[] cancelTakerTokenAmounts)
|
||||
public
|
||||
{
|
||||
for (uint i = 0; i < orderAddresses.length; i++) {
|
||||
cancelOrder(
|
||||
orderAddresses[i],
|
||||
orderValues[i],
|
||||
cancelTakerTokenAmounts[i]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Constant public functions
|
||||
*/
|
||||
|
||||
/// @dev Calculates Keccak-256 hash of order with specified parameters.
|
||||
/// @param orderAddresses Array of order's maker, taker, makerToken, takerToken, and feeRecipient.
|
||||
/// @param orderValues Array of order's makerTokenAmount, takerTokenAmount, makerFee, takerFee, expirationTimestampInSec, and salt.
|
||||
/// @return Keccak-256 hash of order.
|
||||
function getOrderHash(address[5] orderAddresses, uint[6] orderValues)
|
||||
public
|
||||
constant
|
||||
returns (bytes32)
|
||||
{
|
||||
return keccak256(
|
||||
address(this),
|
||||
orderAddresses[0], // maker
|
||||
orderAddresses[1], // taker
|
||||
orderAddresses[2], // makerToken
|
||||
orderAddresses[3], // takerToken
|
||||
orderAddresses[4], // feeRecipient
|
||||
orderValues[0], // makerTokenAmount
|
||||
orderValues[1], // takerTokenAmount
|
||||
orderValues[2], // makerFee
|
||||
orderValues[3], // takerFee
|
||||
orderValues[4], // expirationTimestampInSec
|
||||
orderValues[5] // salt
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Verifies that an order signature is valid.
|
||||
/// @param signer address of signer.
|
||||
/// @param hash Signed Keccak-256 hash.
|
||||
/// @param v ECDSA signature parameter v.
|
||||
/// @param r ECDSA signature parameters r.
|
||||
/// @param s ECDSA signature parameters s.
|
||||
/// @return Validity of order signature.
|
||||
function isValidSignature(
|
||||
address signer,
|
||||
bytes32 hash,
|
||||
uint8 v,
|
||||
bytes32 r,
|
||||
bytes32 s)
|
||||
public
|
||||
constant
|
||||
returns (bool)
|
||||
{
|
||||
return signer == ecrecover(
|
||||
keccak256("\x19Ethereum Signed Message:\n32", hash),
|
||||
v,
|
||||
r,
|
||||
s
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Checks if rounding error > 0.1%.
|
||||
/// @param numerator Numerator.
|
||||
/// @param denominator Denominator.
|
||||
/// @param target Value to multiply with numerator/denominator.
|
||||
/// @return Rounding error is present.
|
||||
function isRoundingError(uint numerator, uint denominator, uint target)
|
||||
public
|
||||
constant
|
||||
returns (bool)
|
||||
{
|
||||
uint remainder = mulmod(target, numerator, denominator);
|
||||
if (remainder == 0) return false; // No rounding error.
|
||||
|
||||
uint errPercentageTimes1000000 = safeDiv(
|
||||
safeMul(remainder, 1000000),
|
||||
safeMul(numerator, target)
|
||||
);
|
||||
return errPercentageTimes1000000 > 1000;
|
||||
}
|
||||
|
||||
/// @dev Calculates partial value given a numerator and denominator.
|
||||
/// @param numerator Numerator.
|
||||
/// @param denominator Denominator.
|
||||
/// @param target Value to calculate partial of.
|
||||
/// @return Partial value of target.
|
||||
function getPartialAmount(uint numerator, uint denominator, uint target)
|
||||
public
|
||||
constant
|
||||
returns (uint)
|
||||
{
|
||||
return safeDiv(safeMul(numerator, target), denominator);
|
||||
}
|
||||
|
||||
/// @dev Calculates the sum of values already filled and cancelled for a given order.
|
||||
/// @param orderHash The Keccak-256 hash of the given order.
|
||||
/// @return Sum of values already filled and cancelled.
|
||||
function getUnavailableTakerTokenAmount(bytes32 orderHash)
|
||||
public
|
||||
constant
|
||||
returns (uint)
|
||||
{
|
||||
return safeAdd(filled[orderHash], cancelled[orderHash]);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Internal functions
|
||||
*/
|
||||
|
||||
/// @dev Transfers a token using TokenTransferProxy transferFrom function.
|
||||
/// @param token Address of token to transferFrom.
|
||||
/// @param from Address transfering token.
|
||||
/// @param to Address receiving token.
|
||||
/// @param value Amount of token to transfer.
|
||||
/// @return Success of token transfer.
|
||||
function transferViaTokenTransferProxy(
|
||||
address token,
|
||||
address from,
|
||||
address to,
|
||||
uint value)
|
||||
internal
|
||||
returns (bool)
|
||||
{
|
||||
return TokenTransferProxy(TOKEN_TRANSFER_PROXY_CONTRACT).transferFrom(token, from, to, value);
|
||||
}
|
||||
|
||||
/// @dev Checks if any order transfers will fail.
|
||||
/// @param order Order struct of params that will be checked.
|
||||
/// @param fillTakerTokenAmount Desired amount of takerToken to fill.
|
||||
/// @return Predicted result of transfers.
|
||||
function isTransferable(Order order, uint fillTakerTokenAmount)
|
||||
internal
|
||||
constant // The called token contracts may attempt to change state, but will not be able to due to gas limits on getBalance and getAllowance.
|
||||
returns (bool)
|
||||
{
|
||||
address taker = msg.sender;
|
||||
uint fillMakerTokenAmount = getPartialAmount(fillTakerTokenAmount, order.takerTokenAmount, order.makerTokenAmount);
|
||||
|
||||
if (order.feeRecipient != address(0)) {
|
||||
bool isMakerTokenZRX = order.makerToken == ZRX_TOKEN_CONTRACT;
|
||||
bool isTakerTokenZRX = order.takerToken == ZRX_TOKEN_CONTRACT;
|
||||
uint paidMakerFee = getPartialAmount(fillTakerTokenAmount, order.takerTokenAmount, order.makerFee);
|
||||
uint paidTakerFee = getPartialAmount(fillTakerTokenAmount, order.takerTokenAmount, order.takerFee);
|
||||
uint requiredMakerZRX = isMakerTokenZRX ? safeAdd(fillMakerTokenAmount, paidMakerFee) : paidMakerFee;
|
||||
uint requiredTakerZRX = isTakerTokenZRX ? safeAdd(fillTakerTokenAmount, paidTakerFee) : paidTakerFee;
|
||||
|
||||
if ( getBalance(ZRX_TOKEN_CONTRACT, order.maker) < requiredMakerZRX
|
||||
|| getAllowance(ZRX_TOKEN_CONTRACT, order.maker) < requiredMakerZRX
|
||||
|| getBalance(ZRX_TOKEN_CONTRACT, taker) < requiredTakerZRX
|
||||
|| getAllowance(ZRX_TOKEN_CONTRACT, taker) < requiredTakerZRX
|
||||
) return false;
|
||||
|
||||
if (!isMakerTokenZRX && ( getBalance(order.makerToken, order.maker) < fillMakerTokenAmount // Don't double check makerToken if ZRX
|
||||
|| getAllowance(order.makerToken, order.maker) < fillMakerTokenAmount)
|
||||
) return false;
|
||||
if (!isTakerTokenZRX && ( getBalance(order.takerToken, taker) < fillTakerTokenAmount // Don't double check takerToken if ZRX
|
||||
|| getAllowance(order.takerToken, taker) < fillTakerTokenAmount)
|
||||
) return false;
|
||||
} else if ( getBalance(order.makerToken, order.maker) < fillMakerTokenAmount
|
||||
|| getAllowance(order.makerToken, order.maker) < fillMakerTokenAmount
|
||||
|| getBalance(order.takerToken, taker) < fillTakerTokenAmount
|
||||
|| getAllowance(order.takerToken, taker) < fillTakerTokenAmount
|
||||
) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @dev Get token balance of an address.
|
||||
/// @param token Address of token.
|
||||
/// @param owner Address of owner.
|
||||
/// @return Token balance of owner.
|
||||
function getBalance(address token, address owner)
|
||||
internal
|
||||
constant // The called token contract may attempt to change state, but will not be able to due to an added gas limit.
|
||||
returns (uint)
|
||||
{
|
||||
return Token(token).balanceOf.gas(EXTERNAL_QUERY_GAS_LIMIT)(owner); // Limit gas to prevent reentrancy
|
||||
}
|
||||
|
||||
/// @dev Get allowance of token given to TokenTransferProxy by an address.
|
||||
/// @param token Address of token.
|
||||
/// @param owner Address of owner.
|
||||
/// @return Allowance of token given to TokenTransferProxy by owner.
|
||||
function getAllowance(address token, address owner)
|
||||
internal
|
||||
constant // The called token contract may attempt to change state, but will not be able to due to an added gas limit.
|
||||
returns (uint)
|
||||
{
|
||||
return Token(token).allowance.gas(EXTERNAL_QUERY_GAS_LIMIT)(owner, TOKEN_TRANSFER_PROXY_CONTRACT); // Limit gas to prevent reentrancy
|
||||
}
|
||||
}
|
||||
115
packages/sol-compiler/test/fixtures/contracts/TokenTransferProxy.sol
vendored
Normal file
115
packages/sol-compiler/test/fixtures/contracts/TokenTransferProxy.sol
vendored
Normal 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;
|
||||
}
|
||||
}
|
||||
41
packages/sol-compiler/test/fixtures/contracts/base/SafeMath.sol
vendored
Normal file
41
packages/sol-compiler/test/fixtures/contracts/base/SafeMath.sol
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
pragma solidity ^0.4.14;
|
||||
|
||||
contract SafeMath {
|
||||
function safeMul(uint a, uint b) internal constant returns (uint256) {
|
||||
uint c = a * b;
|
||||
assert(a == 0 || c / a == b);
|
||||
return c;
|
||||
}
|
||||
|
||||
function safeDiv(uint a, uint b) internal constant returns (uint256) {
|
||||
uint c = a / b;
|
||||
return c;
|
||||
}
|
||||
|
||||
function safeSub(uint a, uint b) internal constant returns (uint256) {
|
||||
assert(b <= a);
|
||||
return a - b;
|
||||
}
|
||||
|
||||
function safeAdd(uint a, uint b) internal constant returns (uint256) {
|
||||
uint c = a + b;
|
||||
assert(c >= a);
|
||||
return c;
|
||||
}
|
||||
|
||||
function max64(uint64 a, uint64 b) internal constant returns (uint64) {
|
||||
return a >= b ? a : b;
|
||||
}
|
||||
|
||||
function min64(uint64 a, uint64 b) internal constant returns (uint64) {
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
function max256(uint256 a, uint256 b) internal constant returns (uint256) {
|
||||
return a >= b ? a : b;
|
||||
}
|
||||
|
||||
function min256(uint256 a, uint256 b) internal constant returns (uint256) {
|
||||
return a < b ? a : b;
|
||||
}
|
||||
}
|
||||
38
packages/sol-compiler/test/fixtures/contracts/base/Token.sol
vendored
Normal file
38
packages/sol-compiler/test/fixtures/contracts/base/Token.sol
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
pragma solidity ^0.4.14;
|
||||
|
||||
contract Token {
|
||||
|
||||
/// @return total amount of tokens
|
||||
function totalSupply() constant returns (uint supply) {}
|
||||
|
||||
/// @param _owner The address from which the balance will be retrieved
|
||||
/// @return The balance
|
||||
function balanceOf(address _owner) constant returns (uint balance) {}
|
||||
|
||||
/// @notice send `_value` token to `_to` from `msg.sender`
|
||||
/// @param _to The address of the recipient
|
||||
/// @param _value The amount of token to be transferred
|
||||
/// @return Whether the transfer was successful or not
|
||||
function transfer(address _to, uint _value) returns (bool success) {}
|
||||
|
||||
/// @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from`
|
||||
/// @param _from The address of the sender
|
||||
/// @param _to The address of the recipient
|
||||
/// @param _value The amount of token to be transferred
|
||||
/// @return Whether the transfer was successful or not
|
||||
function transferFrom(address _from, address _to, uint _value) returns (bool success) {}
|
||||
|
||||
/// @notice `msg.sender` approves `_addr` to spend `_value` tokens
|
||||
/// @param _spender The address of the account able to transfer the tokens
|
||||
/// @param _value The amount of wei to be approved for transfer
|
||||
/// @return Whether the approval was successful or not
|
||||
function approve(address _spender, uint _value) returns (bool success) {}
|
||||
|
||||
/// @param _owner The address of the account owning tokens
|
||||
/// @param _spender The address of the account able to transfer the tokens
|
||||
/// @return Amount of remaining tokens allowed to spent
|
||||
function allowance(address _owner, address _spender) constant returns (uint remaining) {}
|
||||
|
||||
event Transfer(address indexed _from, address indexed _to, uint _value);
|
||||
event Approval(address indexed _owner, address indexed _spender, uint _value);
|
||||
}
|
||||
4
packages/sol-compiler/test/fixtures/exchange_bin.ts
vendored
Normal file
4
packages/sol-compiler/test/fixtures/exchange_bin.ts
vendored
Normal file
File diff suppressed because one or more lines are too long
11
packages/sol-compiler/test/util/constants.ts
Normal file
11
packages/sol-compiler/test/util/constants.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { BigNumber } from '@0xproject/utils';
|
||||
|
||||
export const constants = {
|
||||
networkId: 0,
|
||||
optimizerEnabled: false,
|
||||
gasPrice: new BigNumber(20000000000),
|
||||
timeoutMs: 30000,
|
||||
zrxTokenAddress: '0xe41d2489571d322189246dafa5ebde1f4699f498',
|
||||
tokenTransferProxyAddress: '0x8da0d80f5007ef1e431dd2127178d224e32c2ef4',
|
||||
contracts: '*' as '*',
|
||||
};
|
||||
9
packages/sol-compiler/test/util/provider.ts
Normal file
9
packages/sol-compiler/test/util/provider.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { web3Factory } from '@0xproject/dev-utils';
|
||||
import { Provider } from '@0xproject/types';
|
||||
import * as Web3 from 'web3';
|
||||
|
||||
const providerConfigs = { shouldUseInProcessGanache: true };
|
||||
const web3Instance = web3Factory.create(providerConfigs);
|
||||
const provider: Provider = web3Instance.currentProvider;
|
||||
|
||||
export { provider };
|
||||
8
packages/sol-compiler/tsconfig.json
Normal file
8
packages/sol-compiler/tsconfig.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "../../tsconfig",
|
||||
"compilerOptions": {
|
||||
"outDir": "lib",
|
||||
"strictFunctionTypes": false
|
||||
},
|
||||
"include": ["./src/**/*", "./test/**/*"]
|
||||
}
|
||||
3
packages/sol-compiler/tslint.json
Normal file
3
packages/sol-compiler/tslint.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": ["@0xproject/tslint-config"]
|
||||
}
|
||||
Reference in New Issue
Block a user