diff options
author | Fabio Berger <me@fabioberger.com> | 2018-05-10 23:08:07 +0800 |
---|---|---|
committer | Fabio Berger <me@fabioberger.com> | 2018-05-10 23:08:07 +0800 |
commit | cd5f00ac4d41221c99eb8ce767e63e09a6de6a11 (patch) | |
tree | 2b1865b5fa6ef86965ea753c032ffaba48403921 | |
parent | 23c4027c83b9f30fad352615386b988084f8b39f (diff) | |
parent | c64ad1af28ef116e210aafb3ea6ad2138361cd7c (diff) | |
download | dexon-0x-contracts-cd5f00ac4d41221c99eb8ce767e63e09a6de6a11.tar.gz dexon-0x-contracts-cd5f00ac4d41221c99eb8ce767e63e09a6de6a11.tar.zst dexon-0x-contracts-cd5f00ac4d41221c99eb8ce767e63e09a6de6a11.zip |
Merge branch 'development' into breakUp0xjs
* development: (38 commits)
Add fallback image support to relayer grid tile
Clear relayer grid state when fetching
Configure the compiler to generate artifacts with deployedBytecode
Implement loading and error state for relayer grid
Fallback image for relayer grid tile
Change relayer grid tile to link on header
Display top tokens from backend
Remove overflowZ property from portal
Suggestions and fix bad merge
Fix typo
Only show untracked tokens
Make wallet scrollable
Add token flow
Update The Ocean logo
Fix artifacts paths
Create an artifacts folder
Introduce a var
Add removeHexPrefix util method
CHeck if ABI exists
Improve the readability of the check for should compile
...
# Conflicts:
# .gitignore
# 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
46 files changed, 983 insertions, 509 deletions
diff --git a/.gitignore b/.gitignore index e798e3a0a..ab90e9e17 100644 --- a/.gitignore +++ b/.gitignore @@ -79,7 +79,7 @@ packages/metacoin/artifacts packages/0x.js/test/artifacts packages/order-watcher/test/artifacts packages/contract-wrappers/test/artifacts -packages/migrations/src/artifacts +packages/migrations/artifacts # generated contract wrappers packages/0x.js/src/contract_wrappers/generated/ diff --git a/.prettierignore b/.prettierignore index 024d0b548..f12fdf870 100644 --- a/.prettierignore +++ b/.prettierignore @@ -2,6 +2,6 @@ lib .nyc_output /packages/contracts/src/artifacts /packages/metacoin/artifacts -/packages/migrations/src/artifacts +/packages/migrations/artifacts/1.0.0 package.json scripts/postpublish_utils.js diff --git a/packages/0x.js/package.json b/packages/0x.js/package.json index 9fe2ec1a4..d1091b9e9 100644 --- a/packages/0x.js/package.json +++ b/packages/0x.js/package.json @@ -21,7 +21,7 @@ "test": "run-s clean test:commonjs", "test:coverage": "nyc npm run test --all && yarn coverage:report:lcov", "coverage:report:lcov": "nyc report --reporter=text-lcov > coverage/lcov.info", - "update_artifacts": "for i in ${npm_package_config_contracts}; do copyfiles -u 4 ../migrations/src/artifacts/$i.json test/artifacts; done;", + "update_artifacts": "for i in ${npm_package_config_contracts}; do copyfiles -u 4 ../migrations/artifacts/1.0.0/$i.json test/artifacts; done;", "clean": "shx rm -rf _bundles lib test_temp scripts", "build:umd:prod": "NODE_ENV=production webpack", "build:commonjs": "tsc && yarn update_artifacts && copyfiles -u 2 './src/compact_artifacts/**/*.json' ./lib/src/compact_artifacts && copyfiles -u 3 './lib/src/monorepo_scripts/**/*' ./scripts", diff --git a/packages/abi-gen/src/index.ts b/packages/abi-gen/src/index.ts index ecef33b16..7125171b9 100644 --- a/packages/abi-gen/src/index.ts +++ b/packages/abi-gen/src/index.ts @@ -108,8 +108,8 @@ for (const abiFileName of abiFileNames) { ABI = parsedContent; // ABI file } else if (!_.isUndefined(parsedContent.abi)) { ABI = parsedContent.abi; // Truffle artifact - } else if (!_.isUndefined(parsedContent.networks) && !_.isUndefined(parsedContent.networks[args.networkId])) { - ABI = parsedContent.networks[args.networkId].abi; // 0x contracts package artifact + } else if (!_.isUndefined(parsedContent.compilerOutput.abi)) { + ABI = parsedContent.compilerOutput.abi; // 0x artifact } if (_.isUndefined(ABI)) { logUtils.log(`${chalk.red(`ABI not found in ${abiFileName}.`)}`); diff --git a/packages/contracts/compiler.json b/packages/contracts/compiler.json new file mode 100644 index 000000000..493d6373d --- /dev/null +++ b/packages/contracts/compiler.json @@ -0,0 +1,33 @@ +{ + "artifactsDir": "../migrations/artifacts/1.0.0", + "contractsDir": "src/contracts", + "compilerSettings": { + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode.object", + "evm.bytecode.sourceMap", + "evm.deployedBytecode.object", + "evm.deployedBytecode.sourceMap" + ] + } + } + }, + "contracts": [ + "Exchange", + "DummyToken", + "ZRXToken", + "Token", + "WETH9", + "TokenTransferProxy", + "MultiSigWallet", + "MultiSigWalletWithTimeLock", + "MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress", + "MaliciousToken", + "TokenRegistry", + "Arbitrage", + "EtherDelta", + "AccountLevels" + ] +} diff --git a/packages/contracts/package.json b/packages/contracts/package.json index 6d51d71cb..c47518f17 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -10,13 +10,13 @@ "scripts": { "build:watch": "tsc -w", "prebuild": "run-s clean compile copy_artifacts generate_contract_wrappers", - "copy_artifacts": "copyfiles -u 4 '../migrations/src/artifacts/**/*' ./lib/src/artifacts;", + "copy_artifacts": "copyfiles -u 4 '../migrations/artifacts/1.0.0/**/*' ./lib/src/artifacts;", "build": "tsc", "test": "run-s build run_mocha", "test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov", "run_mocha": "mocha 'lib/test/**/*.js' --timeout 100000 --bail --exit", "compile:comment": "Yarn workspaces do not link binaries correctly so we need to reference them directly https://github.com/yarnpkg/yarn/issues/3846", - "compile": "node ../deployer/lib/src/cli.js compile --contracts ${npm_package_config_contracts} --contracts-dir src/contracts --artifacts-dir ../migrations/src/artifacts", + "compile": "node ../deployer/lib/src/cli.js compile", "clean": "shx rm -rf ./lib", "generate_contract_wrappers": "node ../abi-gen/lib/index.js --abis ${npm_package_config_abis} --template ../contract_templates/contract.handlebars --partials '../contract_templates/partials/**/*.handlebars' --output src/contract_wrappers/generated --backend ethers && prettier --write 'src/contract_wrappers/generated/**.ts'", "lint": "tslint --project . 'migrations/**/*.ts' 'test/**/*.ts' 'util/**/*.ts' 'deploy/**/*.ts'", @@ -26,8 +26,7 @@ "test:circleci": "yarn test:coverage" }, "config": { - "abis": "../migrations/src/artifacts/@(DummyToken|TokenTransferProxy|Exchange|TokenRegistry|MultiSigWallet|MultiSigWalletWithTimeLock|MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress|TokenRegistry|ZRXToken|Arbitrage|EtherDelta|AccountLevels).json", - "contracts": "Exchange,DummyToken,ZRXToken,Token,WETH9,TokenTransferProxy,MultiSigWallet,MultiSigWalletWithTimeLock,MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress,MaliciousToken,TokenRegistry,Arbitrage,EtherDelta,AccountLevels" + "abis": "../migrations/artifacts/1.0.0/@(DummyToken|TokenTransferProxy|Exchange|TokenRegistry|MultiSigWallet|MultiSigWalletWithTimeLock|MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress|TokenRegistry|ZRXToken|Arbitrage|EtherDelta|AccountLevels).json" }, "repository": { "type": "git", diff --git a/packages/contracts/test/multi_sig_with_time_lock.ts b/packages/contracts/test/multi_sig_with_time_lock.ts index 0f1e3bd11..cf3893780 100644 --- a/packages/contracts/test/multi_sig_with_time_lock.ts +++ b/packages/contracts/test/multi_sig_with_time_lock.ts @@ -17,7 +17,7 @@ import { chaiSetup } from './utils/chai_setup'; import { deployer } from './utils/deployer'; import { provider, web3Wrapper } from './utils/web3_wrapper'; -const MULTI_SIG_ABI = artifacts.MultiSigWalletWithTimeLock.networks[constants.TESTRPC_NETWORK_ID].abi; +const MULTI_SIG_ABI = artifacts.MultiSigWalletWithTimeLock.compilerOutput.abi; chaiSetup.configure(); const expect = chai.expect; const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); diff --git a/packages/contracts/test/multi_sig_with_time_lock_except_remove_auth_addr.ts b/packages/contracts/test/multi_sig_with_time_lock_except_remove_auth_addr.ts index 006d7f3c6..3ff5b82ed 100644 --- a/packages/contracts/test/multi_sig_with_time_lock_except_remove_auth_addr.ts +++ b/packages/contracts/test/multi_sig_with_time_lock_except_remove_auth_addr.ts @@ -17,10 +17,9 @@ import { ContractName, SubmissionContractEventArgs, TransactionDataParams } from import { chaiSetup } from './utils/chai_setup'; import { deployer } from './utils/deployer'; import { provider, web3Wrapper } from './utils/web3_wrapper'; -const PROXY_ABI = artifacts.TokenTransferProxy.networks[constants.TESTRPC_NETWORK_ID].abi; +const PROXY_ABI = artifacts.TokenTransferProxy.compilerOutput.abi; const MUTISIG_WALLET_WITH_TIME_LOCK_EXCEPT_REMOVE_AUTHORIZED_ADDRESS_ABI = - artifacts.MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress.networks[constants.TESTRPC_NETWORK_ID] - .abi; + artifacts.MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress.compilerOutput.abi; chaiSetup.configure(); const expect = chai.expect; diff --git a/packages/contracts/util/artifacts.ts b/packages/contracts/util/artifacts.ts index f427c6d9a..d262b8058 100644 --- a/packages/contracts/util/artifacts.ts +++ b/packages/contracts/util/artifacts.ts @@ -1,3 +1,5 @@ +import { ContractArtifact } from '@0xproject/deployer'; + import * as DummyToken from '../src/artifacts/DummyToken.json'; import * as Exchange from '../src/artifacts/Exchange.json'; import * as MaliciousToken from '../src/artifacts/MaliciousToken.json'; @@ -9,17 +11,15 @@ import * as TokenTransferProxy from '../src/artifacts/TokenTransferProxy.json'; import * as EtherToken from '../src/artifacts/WETH9.json'; import * as ZRX from '../src/artifacts/ZRXToken.json'; -import { Artifact } from './types'; - export const artifacts = { - ZRX: (ZRX as any) as Artifact, - DummyToken: (DummyToken as any) as Artifact, - Token: (Token as any) as Artifact, - Exchange: (Exchange as any) as Artifact, - EtherToken: (EtherToken as any) as Artifact, - TokenRegistry: (TokenRegistry as any) as Artifact, - MaliciousToken: (MaliciousToken as any) as Artifact, - TokenTransferProxy: (TokenTransferProxy as any) as Artifact, - MultiSigWalletWithTimeLock: (MultiSigWalletWithTimeLock as any) as Artifact, - MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress: (MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress as any) as Artifact, + ZRX: (ZRX as any) as ContractArtifact, + DummyToken: (DummyToken as any) as ContractArtifact, + Token: (Token as any) as ContractArtifact, + Exchange: (Exchange as any) as ContractArtifact, + EtherToken: (EtherToken as any) as ContractArtifact, + TokenRegistry: (TokenRegistry as any) as ContractArtifact, + MaliciousToken: (MaliciousToken as any) as ContractArtifact, + TokenTransferProxy: (TokenTransferProxy as any) as ContractArtifact, + MultiSigWalletWithTimeLock: (MultiSigWalletWithTimeLock as any) as ContractArtifact, + MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress: (MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress as any) as ContractArtifact, }; diff --git a/packages/contracts/util/types.ts b/packages/contracts/util/types.ts index 321084c42..9bc412433 100644 --- a/packages/contracts/util/types.ts +++ b/packages/contracts/util/types.ts @@ -100,19 +100,3 @@ export enum ContractName { EtherDelta = 'EtherDelta', Arbitrage = 'Arbitrage', } - -export interface Artifact { - contract_name: ContractName; - networks: { - [networkId: number]: { - abi: ContractAbi; - solc_version: string; - keccak256: string; - optimizer_enabled: number; - unlinked_binary: string; - updated_at: number; - address: string; - constructor_args: string; - }; - }; -} diff --git a/packages/deployer/src/cli.ts b/packages/deployer/src/cli.ts index 7f1ae708a..8c89cf382 100644 --- a/packages/deployer/src/cli.ts +++ b/packages/deployer/src/cli.ts @@ -13,13 +13,13 @@ import { constants } from './utils/constants'; import { consoleReporter } from './utils/error_reporter'; import { CliOptions, CompilerOptions, DeployerOptions } from './utils/types'; -const DEFAULT_OPTIMIZER_ENABLED = false; const DEFAULT_CONTRACTS_DIR = path.resolve('src/contracts'); const DEFAULT_ARTIFACTS_DIR = path.resolve('src/artifacts'); const DEFAULT_NETWORK_ID = 50; const DEFAULT_JSONRPC_URL = 'http://localhost:8545'; const DEFAULT_GAS_PRICE = (10 ** 9 * 2).toString(); const DEFAULT_CONTRACTS_LIST = '*'; +const SEPARATOR = ','; /** * Compiles all contracts with options passed in through CLI. @@ -28,10 +28,8 @@ const DEFAULT_CONTRACTS_LIST = '*'; async function onCompileCommandAsync(argv: CliOptions): Promise<void> { const opts: CompilerOptions = { contractsDir: argv.contractsDir, - networkId: argv.networkId, - optimizerEnabled: argv.shouldOptimize, artifactsDir: argv.artifactsDir, - specifiedContracts: getContractsSetFromList(argv.contracts), + contracts: argv.contracts === DEFAULT_CONTRACTS_LIST ? DEFAULT_CONTRACTS_LIST : argv.contracts.split(SEPARATOR), }; await commands.compileAsync(opts); } @@ -46,10 +44,8 @@ async function onDeployCommandAsync(argv: CliOptions): Promise<void> { const networkId = await web3Wrapper.getNetworkIdAsync(); const compilerOpts: CompilerOptions = { contractsDir: argv.contractsDir, - networkId, - optimizerEnabled: argv.shouldOptimize, artifactsDir: argv.artifactsDir, - specifiedContracts: getContractsSetFromList(argv.contracts), + contracts: argv.contracts === DEFAULT_CONTRACTS_LIST ? DEFAULT_CONTRACTS_LIST : argv.contracts.split(SEPARATOR), }; await commands.compileAsync(compilerOpts); @@ -58,91 +54,79 @@ async function onDeployCommandAsync(argv: CliOptions): Promise<void> { from: argv.account, }; const deployerOpts: DeployerOptions = { - artifactsDir: argv.artifactsDir, + artifactsDir: argv.artifactsDir || DEFAULT_ARTIFACTS_DIR, jsonrpcUrl: argv.jsonrpcUrl, networkId, defaults, }; - const deployerArgsString = argv.args as string; - const deployerArgs = deployerArgsString.split(','); + const deployerArgsString = argv.constructorArgs as string; + const deployerArgs = deployerArgsString.split(SEPARATOR); await commands.deployAsync(argv.contract as string, deployerArgs, deployerOpts); } /** - * Creates a set of contracts to compile. - * @param contracts Comma separated list of contracts to compile - */ -function getContractsSetFromList(contracts: string): Set<string> { - const specifiedContracts = new Set(); - if (contracts === '*') { - return new Set(['*']); - } - const contractsArray = contracts.split(','); - _.forEach(contractsArray, contractName => { - specifiedContracts.add(contractName); - }); - return specifiedContracts; -} -/** - * Provides extra required options for deploy command. + * Adds additional required options for when the user is calling the deploy command. * @param yargsInstance yargs instance provided in builder function callback. */ function deployCommandBuilder(yargsInstance: any) { return yargsInstance - .option('contract', { - type: 'string', - description: 'name of contract to deploy, exluding .sol extension', - }) - .option('args', { - type: 'string', - description: 'comma separated list of constructor args to deploy contract with', - }) - .demandOption(['contract', 'args']) - .help().argv; -} - -(() => { - const identityCommandBuilder = _.identity; - return yargs - .option('contracts-dir', { - type: 'string', - default: DEFAULT_CONTRACTS_DIR, - description: 'path of contracts directory to compile', - }) .option('network-id', { type: 'number', default: DEFAULT_NETWORK_ID, description: 'mainnet=1, kovan=42, testrpc=50', }) - .option('should-optimize', { - type: 'boolean', - default: DEFAULT_OPTIMIZER_ENABLED, - description: 'enable optimizer', + .option('contract', { + type: 'string', + description: 'name of contract to deploy, excluding .sol extension', }) - .option('artifacts-dir', { + .option('constructor-args', { type: 'string', - default: DEFAULT_ARTIFACTS_DIR, - description: 'path to write contracts artifacts to', + description: 'comma separated list of constructor args to deploy contract with', }) .option('jsonrpc-url', { type: 'string', default: DEFAULT_JSONRPC_URL, description: 'url of JSON RPC', }) + .option('account', { + type: 'string', + description: 'account to use for deploying contracts', + }) .option('gas-price', { type: 'string', default: DEFAULT_GAS_PRICE, description: 'gasPrice to be used for transactions', }) - .option('account', { - type: 'string', - description: 'account to use for deploying contracts', - }) + .demandOption(['contract', 'args', 'account']) + .help().argv; +} + +/** + * Adds additional required options for when the user is calling the compile command. + * @param yargsInstance yargs instance provided in builder function callback. + */ +function compileCommandBuilder(yargsInstance: any) { + return yargsInstance .option('contracts', { type: 'string', default: DEFAULT_CONTRACTS_LIST, description: 'comma separated list of contracts to compile', }) - .command('compile', 'compile contracts', identityCommandBuilder, consoleReporter(onCompileCommandAsync)) + .help().argv; +} + +(() => { + const identityCommandBuilder = _.identity; + return 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', + }) + .demandCommand(1) + .command('compile', 'compile contracts', compileCommandBuilder, consoleReporter(onCompileCommandAsync)) .command( 'deploy', 'deploy a single contract with provided arguments', diff --git a/packages/deployer/src/compiler.ts b/packages/deployer/src/compiler.ts index a3c8004ec..e81df3c0a 100644 --- a/packages/deployer/src/compiler.ts +++ b/packages/deployer/src/compiler.ts @@ -6,6 +6,7 @@ import { FSResolver, NameResolver, NPMResolver, + RelativeFSResolver, Resolver, URLResolver, } from '@0xproject/sol-resolver'; @@ -38,11 +39,30 @@ import { 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 @@ -52,26 +72,28 @@ export class Compiler { private _resolver: Resolver; private _nameResolver: NameResolver; private _contractsDir: string; - private _networkId: number; - private _optimizerEnabled: boolean; + private _compilerSettings: solc.CompilerSettings; private _artifactsDir: string; - private _specifiedContracts: Set<string> = new Set(); + private _specifiedContracts: string[] | TYPE_ALL_FILES_IDENTIFIER; /** * Instantiates a new instance of the Compiler class. - * @param opts Options specifying directories, network, and optimization settings. * @return An instance of the Compiler class. */ constructor(opts: CompilerOptions) { - this._contractsDir = opts.contractsDir; - this._networkId = opts.networkId; - this._optimizerEnabled = opts.optimizerEnabled; - this._artifactsDir = opts.artifactsDir; - this._specifiedContracts = opts.specifiedContracts; + // 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; @@ -83,13 +105,13 @@ export class Compiler { await createDirIfDoesNotExistAsync(this._artifactsDir); await createDirIfDoesNotExistAsync(SOLC_BIN_DIR); let contractNamesToCompile: string[] = []; - if (this._specifiedContracts.has(ALL_CONTRACTS_IDENTIFIER)) { + 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 = Array.from(this._specifiedContracts.values()); + contractNamesToCompile = this._specifiedContracts; } for (const contractNameToCompile of contractNamesToCompile) { await this._compileContractAsync(contractNameToCompile); @@ -101,17 +123,18 @@ export class Compiler { */ 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(contractSource.path).toString('hex')}`; - + const sourceTreeHashHex = `0x${this._getSourceTreeHash(absoluteContractPath).toString('hex')}`; let shouldCompile = false; if (_.isUndefined(currentArtifactIfExists)) { shouldCompile = true; } else { const currentArtifact = currentArtifactIfExists as ContractArtifact; - shouldCompile = - currentArtifact.networks[this._networkId].optimizer_enabled !== this._optimizerEnabled || - currentArtifact.networks[this._networkId].source_tree_hash !== sourceTreeHashHex; + 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; @@ -139,30 +162,14 @@ export class Compiler { logUtils.log(`Compiling ${contractName} with Solidity v${solcVersion}...`); const source = contractSource.source; - const absoluteFilePath = contractSource.path; const standardInput: solc.StandardInput = { language: 'Solidity', sources: { - [absoluteFilePath]: { - urls: [`file://${absoluteFilePath}`], - }, - }, - settings: { - optimizer: { - enabled: this._optimizerEnabled, - }, - outputSelection: { - '*': { - '*': [ - 'abi', - 'evm.bytecode.object', - 'evm.bytecode.sourceMap', - 'evm.deployedBytecode.object', - 'evm.deployedBytecode.sourceMap', - ], - }, + [contractSource.path]: { + content: contractSource.source, }, }, + settings: this._compilerSettings, }; const compiled: solc.StandardOutput = JSON.parse( solcInstance.compileStandardWrapper(JSON.stringify(standardInput), importPath => { @@ -188,34 +195,28 @@ export class Compiler { }); } } - const compiledData = compiled.contracts[absoluteFilePath][contractName]; + const compiledData = compiled.contracts[contractSource.path][contractName]; if (_.isUndefined(compiledData)) { throw new Error( - `Contract ${contractName} not found in ${absoluteFilePath}. Please make sure your contract has the same name as it's file name`, + `Contract ${contractName} not found in ${ + contractSource.path + }. Please make sure your contract has the same name as it's file name`, ); } - const abi: ContractAbi = compiledData.abi; - const bytecode = `0x${compiledData.evm.bytecode.object}`; - const runtimeBytecode = `0x${compiledData.evm.deployedBytecode.object}`; - const sourceMap = compiledData.evm.bytecode.sourceMap; - const sourceMapRuntime = compiledData.evm.deployedBytecode.sourceMap; - const unresolvedSourcePaths = _.keys(compiled.sources); - const sources = _.map( - unresolvedSourcePaths, - unresolvedSourcePath => this._resolver.resolve(unresolvedSourcePath).path, + const sourceCodes = _.mapValues( + compiled.sources, + (_1, sourceFilePath) => this._resolver.resolve(sourceFilePath).source, ); - const updated_at = Date.now(); - const contractNetworkData: ContractNetworkData = { - solc_version: solcVersion, - source_tree_hash: sourceTreeHashHex, - optimizer_enabled: this._optimizerEnabled, - abi, - bytecode, - runtime_bytecode: runtimeBytecode, - updated_at, - source_map: sourceMap, - source_map_runtime: sourceMapRuntime, - sources, + const contractVersion: ContractVersionData = { + compilerOutput: compiledData, + sources: compiled.sources, + sourceCodes, + sourceTreeHashHex, + compiler: { + name: 'solc', + version: solcVersion, + settings: this._compilerSettings, + }, }; let newArtifact: ContractArtifact; @@ -223,17 +224,14 @@ export class Compiler { const currentArtifact = currentArtifactIfExists as ContractArtifact; newArtifact = { ...currentArtifact, - networks: { - ...currentArtifact.networks, - [this._networkId]: contractNetworkData, - }, + ...contractVersion, }; } else { newArtifact = { - contract_name: contractName, - networks: { - [this._networkId]: contractNetworkData, - }, + schemaVersion: constants.LATEST_ARTIFACT_VERSION, + contractName, + ...contractVersion, + networks: {}, }; } diff --git a/packages/deployer/src/deployer.ts b/packages/deployer/src/deployer.ts index ad05417b1..c8c3a9a06 100644 --- a/packages/deployer/src/deployer.ts +++ b/packages/deployer/src/deployer.ts @@ -2,6 +2,7 @@ import { AbiType, ConstructorAbi, ContractAbi, Provider, TxData } from '@0xproje import { logUtils } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import * as _ from 'lodash'; +import * as solc from 'solc'; import * as Web3 from 'web3'; import { Contract } from './utils/contract'; @@ -28,7 +29,20 @@ export class Deployer { private _artifactsDir: string; private _networkId: number; private _defaults: Partial<TxData>; - + /** + * Gets data for current version stored in artifact. + * @param contractArtifact The contract artifact. + * @return Version specific contract data. + */ + private static _getContractCompilerOutputFromArtifactIfExists( + contractArtifact: ContractArtifact, + ): solc.StandardContractOutput { + const compilerOutputIfExists = contractArtifact.compilerOutput; + if (_.isUndefined(compilerOutputIfExists)) { + throw new Error(`Compiler output not found in artifact for contract: ${contractArtifact.contractName}`); + } + return compilerOutputIfExists; + } /** * Instantiate a new instance of the Deployer class. * @param opts Deployer options, including either an RPC url or Provider instance. @@ -58,10 +72,8 @@ export class Deployer { */ public async deployAsync(contractName: string, args: any[] = []): Promise<Web3.ContractInstance> { const contractArtifactIfExists: ContractArtifact = this._loadContractArtifactIfExists(contractName); - const contractNetworkDataIfExists: ContractNetworkData = this._getContractNetworkDataFromArtifactIfExists( - contractArtifactIfExists, - ); - const data = contractNetworkDataIfExists.bytecode; + const compilerOutput = Deployer._getContractCompilerOutputFromArtifactIfExists(contractArtifactIfExists); + const data = compilerOutput.evm.bytecode.object; const from = await this._getFromAddressAsync(); const gas = await this._getAllowableGasEstimateAsync(data); const txData = { @@ -70,7 +82,10 @@ export class Deployer { data, gas, }; - const abi = contractNetworkDataIfExists.abi; + if (_.isUndefined(compilerOutput.abi)) { + throw new Error(`ABI not found in ${contractName} artifacts`); + } + const abi = compilerOutput.abi; const constructorAbi = _.find(abi, { type: AbiType.Constructor }) as ConstructorAbi; const constructorArgs = _.isUndefined(constructorAbi) ? [] : constructorAbi.inputs; if (constructorArgs.length !== args.length) { @@ -138,15 +153,16 @@ export class Deployer { args: any[], ): Promise<void> { const contractArtifactIfExists: ContractArtifact = this._loadContractArtifactIfExists(contractName); - const contractNetworkDataIfExists: ContractNetworkData = this._getContractNetworkDataFromArtifactIfExists( - contractArtifactIfExists, - ); - const abi = contractNetworkDataIfExists.abi; + const compilerOutput = Deployer._getContractCompilerOutputFromArtifactIfExists(contractArtifactIfExists); + if (_.isUndefined(compilerOutput.abi)) { + throw new Error(`ABI not found in ${contractName} artifacts`); + } + const abi = compilerOutput.abi; const encodedConstructorArgs = encoder.encodeConstructorArgsFromAbi(args, abi); - const newContractData = { - ...contractNetworkDataIfExists, + const newContractData: ContractNetworkData = { address: contractAddress, - constructor_args: encodedConstructorArgs, + links: {}, + constructorArgs: encodedConstructorArgs, }; const newArtifact = { ...contractArtifactIfExists, @@ -174,18 +190,6 @@ export class Deployer { } } /** - * Gets data for current networkId stored in artifact. - * @param contractArtifact The contract artifact. - * @return Network specific contract data. - */ - private _getContractNetworkDataFromArtifactIfExists(contractArtifact: ContractArtifact): ContractNetworkData { - const contractNetworkDataIfExists = contractArtifact.networks[this._networkId]; - if (_.isUndefined(contractNetworkDataIfExists)) { - throw new Error(`Data not found in artifact for contract: ${contractArtifact.contract_name}`); - } - return contractNetworkDataIfExists; - } - /** * Gets the address to use for sending a transaction. * @return The default from address. If not specified, returns the first address accessible by web3. */ diff --git a/packages/deployer/src/index.ts b/packages/deployer/src/index.ts index 186d644ef..31a75677b 100644 --- a/packages/deployer/src/index.ts +++ b/packages/deployer/src/index.ts @@ -1,2 +1,3 @@ export { Deployer } from './deployer'; export { Compiler } from './compiler'; +export { ContractArtifact, ContractNetworks } from './utils/types'; diff --git a/packages/deployer/src/utils/constants.ts b/packages/deployer/src/utils/constants.ts index 6f9dfb994..df2ddb3b2 100644 --- a/packages/deployer/src/utils/constants.ts +++ b/packages/deployer/src/utils/constants.ts @@ -1,4 +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', }; diff --git a/packages/deployer/src/utils/types.ts b/packages/deployer/src/utils/types.ts index a20d0f627..19c27df67 100644 --- a/packages/deployer/src/utils/types.ts +++ b/packages/deployer/src/utils/types.ts @@ -1,4 +1,5 @@ import { ContractAbi, Provider, TxData } from '@0xproject/types'; +import * as solc from 'solc'; import * as Web3 from 'web3'; import * as yargs from 'yargs'; @@ -9,28 +10,40 @@ export enum AbiType { Fallback = 'fallback', } -export interface ContractArtifact { - contract_name: string; +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 { - [key: number]: ContractNetworkData; + [networkId: number]: ContractNetworkData; } export interface ContractNetworkData { - solc_version: string; - optimizer_enabled: boolean; - source_tree_hash: string; - abi: ContractAbi; - bytecode: string; - runtime_bytecode: string; - address?: string; - constructor_args?: string; - updated_at: number; - source_map: string; - source_map_runtime: string; - sources: string[]; + address: string; + links: { + [linkName: string]: string; + }; + constructorArgs: string; } export interface SolcErrors { @@ -42,7 +55,6 @@ export interface CliOptions extends yargs.Arguments { contractsDir: string; jsonrpcUrl: string; networkId: number; - shouldOptimize: boolean; gasPrice: string; account?: string; contract?: string; @@ -50,11 +62,10 @@ export interface CliOptions extends yargs.Arguments { } export interface CompilerOptions { - contractsDir: string; - networkId: number; - optimizerEnabled: boolean; - artifactsDir: string; - specifiedContracts: Set<string>; + contractsDir?: string; + artifactsDir?: string; + compilerSettings?: solc.CompilerSettings; + contracts?: string[] | '*'; } export interface BaseDeployerOptions { diff --git a/packages/deployer/test/compiler_test.ts b/packages/deployer/test/compiler_test.ts index bec111ffa..4c53a871a 100644 --- a/packages/deployer/test/compiler_test.ts +++ b/packages/deployer/test/compiler_test.ts @@ -19,9 +19,7 @@ describe('#Compiler', function() { const compilerOpts: CompilerOptions = { artifactsDir, contractsDir, - networkId: constants.networkId, - optimizerEnabled: constants.optimizerEnabled, - specifiedContracts: new Set(constants.specifiedContracts), + contracts: constants.contracts, }; const compiler = new Compiler(compilerOpts); beforeEach((done: DoneCallback) => { @@ -39,9 +37,8 @@ describe('#Compiler', function() { }; const exchangeArtifactString = await fsWrapper.readFileAsync(exchangeArtifactPath, opts); const exchangeArtifact: ContractArtifact = JSON.parse(exchangeArtifactString); - const exchangeContractData: ContractNetworkData = exchangeArtifact.networks[constants.networkId]; // The last 43 bytes of the binaries are metadata which may not be equivalent - const unlinkedBinaryWithoutMetadata = exchangeContractData.bytecode.slice(0, -86); + const unlinkedBinaryWithoutMetadata = exchangeArtifact.compilerOutput.evm.bytecode.object.slice(0, -86); const exchangeBinaryWithoutMetadata = exchange_binary.slice(0, -86); expect(unlinkedBinaryWithoutMetadata).to.equal(exchangeBinaryWithoutMetadata); }); diff --git a/packages/deployer/test/deployer_test.ts b/packages/deployer/test/deployer_test.ts index d024b0c8c..8c92b38ca 100644 --- a/packages/deployer/test/deployer_test.ts +++ b/packages/deployer/test/deployer_test.ts @@ -20,9 +20,7 @@ describe('#Deployer', () => { const compilerOpts: CompilerOptions = { artifactsDir, contractsDir, - networkId: constants.networkId, - optimizerEnabled: constants.optimizerEnabled, - specifiedContracts: new Set(constants.specifiedContracts), + contracts: constants.contracts, }; const compiler = new Compiler(compilerOpts); const deployerOpts = { @@ -56,8 +54,7 @@ describe('#Deployer', () => { const exchangeContractData: ContractNetworkData = exchangeArtifact.networks[constants.networkId]; const exchangeAddress = exchangeContractInstance.address; expect(exchangeAddress).to.not.equal(undefined); - expect(exchangeContractData.address).to.equal(undefined); - expect(exchangeContractData.constructor_args).to.equal(undefined); + expect(exchangeContractData).to.equal(undefined); }); }); describe('#deployAndSaveAsync', () => { @@ -72,7 +69,7 @@ describe('#Deployer', () => { const exchangeContractData: ContractNetworkData = exchangeArtifact.networks[constants.networkId]; const exchangeAddress = exchangeContractInstance.address; expect(exchangeAddress).to.be.equal(exchangeContractData.address); - expect(constructor_args).to.be.equal(exchangeContractData.constructor_args); + expect(constructor_args).to.be.equal(exchangeContractData.constructorArgs); }); }); }); diff --git a/packages/deployer/test/fixtures/exchange_bin.ts b/packages/deployer/test/fixtures/exchange_bin.ts index 9fbf9bf95..914e76bf5 100644 --- a/packages/deployer/test/fixtures/exchange_bin.ts +++ b/packages/deployer/test/fixtures/exchange_bin.ts @@ -1,4 +1,4 @@ export const constructor_args = '0x000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f4980000000000000000000000008da0d80f5007ef1e431dd2127178d224e32c2ef4'; export const exchange_binary = - '0x608060405234801561001057600080fd5b50604051604080612d998339810180604052810190808051906020019092919080519060200190929190505050816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505050612cca806100cf6000396000f3006080604052600436106100fc576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806314df96ee14610101578063288cdc911461015a5780632ac126221461019f578063363349be146101e4578063394c21e7146103f85780633b30ba59146104975780634f150787146104ee578063741bcc931461071b5780637e9abb50146107cf5780638163681e1461081457806398024a8b146108a6578063add1cbc5146108fb578063b7b2c7d614610952578063baa0181d14610b8b578063bc61394a14610cef578063cfc4d0ec14610dc3578063f06bbf7514610e60578063ffa1ad7414610e93575b600080fd5b34801561010d57600080fd5b50610140600480360381019080803590602001909291908035906020019092919080359060200190929190505050610f23565b604051808215151515815260200191505060405180910390f35b34801561016657600080fd5b506101896004803603810190808035600019169060200190929190505050610f7b565b6040518082815260200191505060405180910390f35b3480156101ab57600080fd5b506101ce6004803603810190808035600019169060200190929190505050610f93565b6040518082815260200191505060405180910390f35b3480156101f057600080fd5b506103e260048036038101908080359060200190820180359060200190808060200260200160405190810160405280939291908181526020016000905b8282101561027257848483905060a00201600580602002604051908101604052809291908260056020028082843782019150505050508152602001906001019061022d565b5050505050919291929080359060200190820180359060200190808060200260200160405190810160405280939291908181526020016000905b828210156102f157848483905060c0020160068060200260405190810160405280929190826006602002808284378201915050505050815260200190600101906102ac565b5050505050919291929080359060200190929190803515159060200190929190803590602001908201803590602001908080602002602001604051908101604052809392919081815260200183836020028082843782019150505050505091929192908035906020019082018035906020019080806020026020016040519081016040528093929190818152602001838360200280828437820191505050505050919291929080359060200190820180359060200190808060200260200160405190810160405280939291908181526020018383602002808284378201915050505050509192919290505050610fab565b6040518082815260200191505060405180910390f35b34801561040457600080fd5b506104816004803603810190808060a001906005806020026040519081016040528092919082600560200280828437820191505050505091929192908060c0019060068060200260405190810160405280929190826006602002808284378201915050505050919291929080359060200190929190505050611110565b6040518082815260200191505060405180910390f35b3480156104a357600080fd5b506104ac6115f8565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156104fa57600080fd5b5061071960048036038101908080359060200190820180359060200190808060200260200160405190810160405280939291908181526020016000905b8282101561057c57848483905060a002016005806020026040519081016040528092919082600560200280828437820191505050505081526020019060010190610537565b5050505050919291929080359060200190820180359060200190808060200260200160405190810160405280939291908181526020016000905b828210156105fb57848483905060c0020160068060200260405190810160405280929190826006602002808284378201915050505050815260200190600101906105b6565b505050505091929192908035906020019082018035906020019080806020026020016040519081016040528093929190818152602001838360200280828437820191505050505050919291929080359060200190820180359060200190808060200260200160405190810160405280939291908181526020018383602002808284378201915050505050509192919290803590602001908201803590602001908080602002602001604051908101604052809392919081815260200183836020028082843782019150505050505091929192908035906020019082018035906020019080806020026020016040519081016040528093929190818152602001838360200280828437820191505050505050919291929050505061161d565b005b34801561072757600080fd5b506107cd6004803603810190808060a001906005806020026040519081016040528092919082600560200280828437820191505050505091929192908060c0019060068060200260405190810160405280929190826006602002808284378201915050505050919291929080359060200190929190803560ff169060200190929190803560001916906020019092919080356000191690602001909291905050506116da565b005b3480156107db57600080fd5b506107fe60048036038101908080356000191690602001909291905050506116ff565b6040518082815260200191505060405180910390f35b34801561082057600080fd5b5061088c600480360381019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035600019169060200190929190803560ff16906020019092919080356000191690602001909291908035600019169060200190929190505050611748565b604051808215151515815260200191505060405180910390f35b3480156108b257600080fd5b506108e5600480360381019080803590602001909291908035906020019092919080359060200190929190505050611849565b6040518082815260200191505060405180910390f35b34801561090757600080fd5b50610910611867565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561095e57600080fd5b50610b8960048036038101908080359060200190820180359060200190808060200260200160405190810160405280939291908181526020016000905b828210156109e057848483905060a00201600580602002604051908101604052809291908260056020028082843782019150505050508152602001906001019061099b565b5050505050919291929080359060200190820180359060200190808060200260200160405190810160405280939291908181526020016000905b82821015610a5f57848483905060c002016006806020026040519081016040528092919082600660200280828437820191505050505081526020019060010190610a1a565b505050505091929192908035906020019082018035906020019080806020026020016040519081016040528093929190818152602001838360200280828437820191505050505050919291929080351515906020019092919080359060200190820180359060200190808060200260200160405190810160405280939291908181526020018383602002808284378201915050505050509192919290803590602001908201803590602001908080602002602001604051908101604052809392919081815260200183836020028082843782019150505050505091929192908035906020019082018035906020019080806020026020016040519081016040528093929190818152602001838360200280828437820191505050505050919291929050505061188d565b005b348015610b9757600080fd5b50610ced60048036038101908080359060200190820180359060200190808060200260200160405190810160405280939291908181526020016000905b82821015610c1957848483905060a002016005806020026040519081016040528092919082600560200280828437820191505050505081526020019060010190610bd4565b5050505050919291929080359060200190820180359060200190808060200260200160405190810160405280939291908181526020016000905b82821015610c9857848483905060c002016006806020026040519081016040528092919082600660200280828437820191505050505081526020019060010190610c53565b505050505091929192908035906020019082018035906020019080806020026020016040519081016040528093929190818152602001838360200280828437820191505050505050919291929050505061194d565b005b348015610cfb57600080fd5b50610dad6004803603810190808060a001906005806020026040519081016040528092919082600560200280828437820191505050505091929192908060c0019060068060200260405190810160405280929190826006602002808284378201915050505050919291929080359060200190929190803515159060200190929190803560ff169060200190929190803560001916906020019092919080356000191690602001909291905050506119c0565b6040518082815260200191505060405180910390f35b348015610dcf57600080fd5b50610e426004803603810190808060a001906005806020026040519081016040528092919082600560200280828437820191505050505091929192908060c00190600680602002604051908101604052809291908260066020028082843782019150505050509192919290505050612160565b60405180826000191660001916815260200191505060405180910390f35b348015610e6c57600080fd5b50610e7561240b565b604051808261ffff1661ffff16815260200191505060405180910390f35b348015610e9f57600080fd5b50610ea8612411565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015610ee8578082015181840152602081019050610ecd565b50505050905090810190601f168015610f155780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b600080600084801515610f3257fe5b86850991506000821415610f495760009250610f72565b610f68610f5983620f424061244a565b610f63888761244a565b61247d565b90506103e8811192505b50509392505050565b60026020528060005260406000206000915090505481565b60036020528060005260406000206000915090505481565b6000806000809150600090505b895181101561110057896000815181101515610fd057fe5b906020019060200201516003600581101515610fe857fe5b602002015173ffffffffffffffffffffffffffffffffffffffff168a8281518110151561101157fe5b90602001906020020151600360058110151561102957fe5b602002015173ffffffffffffffffffffffffffffffffffffffff1614151561105057600080fd5b6110e4826110df8c8481518110151561106557fe5b906020019060200201518c8581518110151561107d57fe5b906020019060200201516110918d88612498565b8c8c888151811015156110a057fe5b906020019060200201518c898151811015156110b857fe5b906020019060200201518c8a8151811015156110d057fe5b906020019060200201516119c0565b6124b1565b9150878214156110f357611100565b8080600101915050610fb8565b8192505050979650505050505050565b600061111a612bd2565b6000806101606040519081016040528088600060058110151561113957fe5b602002015173ffffffffffffffffffffffffffffffffffffffff16815260200188600160058110151561116857fe5b602002015173ffffffffffffffffffffffffffffffffffffffff16815260200188600260058110151561119757fe5b602002015173ffffffffffffffffffffffffffffffffffffffff1681526020018860036005811015156111c657fe5b602002015173ffffffffffffffffffffffffffffffffffffffff1681526020018860046005811015156111f557fe5b602002015173ffffffffffffffffffffffffffffffffffffffff16815260200187600060068110151561122457fe5b6020020151815260200187600160068110151561123d57fe5b6020020151815260200187600260068110151561125657fe5b6020020151815260200187600360068110151561126f57fe5b6020020151815260200187600460068110151561128857fe5b6020020151815260200161129c8989612160565b6000191681525092503373ffffffffffffffffffffffffffffffffffffffff16836000015173ffffffffffffffffffffffffffffffffffffffff161415156112e357600080fd5b60008360a001511180156112fb575060008360c00151115b80156113075750600085115b151561131257600080fd5b8261012001514210151561136f57826101400151600019166000600381111561133757fe5b60ff167f36d86c59e00bd73dc19ba3adfe068e4b64ac7e92be35546adeddf1b956a87e9060405160405180910390a3600093506115ee565b61138a8360c001516113858561014001516116ff565b612498565b915061139685836124cf565b905060008114156113f05782610140015160001916600160038111156113b857fe5b60ff167f36d86c59e00bd73dc19ba3adfe068e4b64ac7e92be35546adeddf1b956a87e9060405160405180910390a3600093506115ee565b61141a600360008561014001516000191660001916815260200190815260200160002054826124b1565b60036000856101400151600019166000191681526020019081526020016000208190555082604001518360600151604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166c010000000000000000000000000281526014018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166c0100000000000000000000000002815260140192505050604051809103902060001916836080015173ffffffffffffffffffffffffffffffffffffffff16846000015173ffffffffffffffffffffffffffffffffffffffff167f67d66f160bc93d925d05dae1794c90d2d6d6688b29b84ff069398a9b0458713186604001518760600151611552878a60c001518b60a00151611849565b878a6101400151604051808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200184815260200183815260200182600019166000191681526020019550505050505060405180910390a48093505b5050509392505050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008090505b86518110156116d1576116c4878281518110151561163d57fe5b90602001906020020151878381518110151561165557fe5b90602001906020020151878481518110151561166d57fe5b90602001906020020151878581518110151561168557fe5b90602001906020020151878681518110151561169d57fe5b9060200190602002015187878151811015156116b557fe5b906020019060200201516116da565b8080600101915050611623565b50505050505050565b836116eb87878760008888886119c0565b1415156116f757600080fd5b505050505050565b600061174160026000846000191660001916815260200190815260200160002054600360008560001916600019168152602001908152602001600020546124b1565b9050919050565b600060018560405180807f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250601c0182600019166000191681526020019150506040518091039020858585604051600081526020016040526040518085600019166000191681526020018460ff1660ff1681526020018360001916600019168152602001826000191660001916815260200194505050505060206040516020810390808403906000865af1158015611806573d6000803e3d6000fd5b5050506020604051035173ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1614905095945050505050565b600061185e611858858461244a565b8461247d565b90509392505050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008090505b87518110156119435761193588828151811015156118ad57fe5b9060200190602002015188838151811015156118c557fe5b9060200190602002015188848151811015156118dd57fe5b906020019060200201518888868151811015156118f657fe5b90602001906020020151888781518110151561190e57fe5b90602001906020020151888881518110151561192657fe5b906020019060200201516119c0565b508080600101915050611893565b5050505050505050565b60008090505b83518110156119ba576119ac848281518110151561196d57fe5b90602001906020020151848381518110151561198557fe5b90602001906020020151848481518110151561199d57fe5b90602001906020020151611110565b508080600101915050611953565b50505050565b60006119ca612bd2565b600080600080610160604051908101604052808e60006005811015156119ec57fe5b602002015173ffffffffffffffffffffffffffffffffffffffff1681526020018e6001600581101515611a1b57fe5b602002015173ffffffffffffffffffffffffffffffffffffffff1681526020018e6002600581101515611a4a57fe5b602002015173ffffffffffffffffffffffffffffffffffffffff1681526020018e6003600581101515611a7957fe5b602002015173ffffffffffffffffffffffffffffffffffffffff1681526020018e6004600581101515611aa857fe5b602002015173ffffffffffffffffffffffffffffffffffffffff1681526020018d6000600681101515611ad757fe5b602002015181526020018d6001600681101515611af057fe5b602002015181526020018d6002600681101515611b0957fe5b602002015181526020018d6003600681101515611b2257fe5b602002015181526020018d6004600681101515611b3b57fe5b60200201518152602001611b4f8f8f612160565b600019168152509450600073ffffffffffffffffffffffffffffffffffffffff16856020015173ffffffffffffffffffffffffffffffffffffffff161480611bc657503373ffffffffffffffffffffffffffffffffffffffff16856020015173ffffffffffffffffffffffffffffffffffffffff16145b1515611bd157600080fd5b60008560a00151118015611be9575060008560c00151115b8015611bf5575060008b115b1515611c0057600080fd5b611c1685600001518661014001518b8b8b611748565b1515611c2157600080fd5b84610120015142101515611c7e578461014001516000191660006003811115611c4657fe5b60ff167f36d86c59e00bd73dc19ba3adfe068e4b64ac7e92be35546adeddf1b956a87e9060405160405180910390a360009550612150565b611c998560c00151611c948761014001516116ff565b612498565b9350611ca58b856124cf565b95506000861415611cff578461014001516000191660016003811115611cc757fe5b60ff167f36d86c59e00bd73dc19ba3adfe068e4b64ac7e92be35546adeddf1b956a87e9060405160405180910390a360009550612150565b611d12868660c001518760a00151610f23565b15611d66578461014001516000191660026003811115611d2e57fe5b60ff167f36d86c59e00bd73dc19ba3adfe068e4b64ac7e92be35546adeddf1b956a87e9060405160405180910390a360009550612150565b89158015611d7b5750611d7985876124e8565b155b15611dce5784610140015160001916600380811115611d9657fe5b60ff167f36d86c59e00bd73dc19ba3adfe068e4b64ac7e92be35546adeddf1b956a87e9060405160405180910390a360009550612150565b611de1868660c001518760a00151611849565b9250611e0d600260008761014001516000191660001916815260200190815260200160002054876124b1565b600260008761014001516000191660001916815260200190815260200160002081905550611e45856040015186600001513386612838565b1515611e5057600080fd5b611e64856060015133876000015189612838565b1515611e6f57600080fd5b600073ffffffffffffffffffffffffffffffffffffffff16856080015173ffffffffffffffffffffffffffffffffffffffff16141515611f6e5760008560e001511115611f0c57611ec9868660c001518760e00151611849565b9150611f006000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff168660000151876080015185612838565b1515611f0b57600080fd5b5b60008561010001511115611f6d57611f2e868660c00151876101000151611849565b9050611f616000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1633876080015184612838565b1515611f6c57600080fd5b5b5b84604001518560600151604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166c010000000000000000000000000281526014018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166c0100000000000000000000000002815260140192505050604051809103902060001916856080015173ffffffffffffffffffffffffffffffffffffffff16866000015173ffffffffffffffffffffffffffffffffffffffff167f0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb33389604001518a60600151898d8a8a8f6101400151604051808973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200186815260200185815260200184815260200183815260200182600019166000191681526020019850505050505050505060405180910390a48595505b5050505050979650505050505050565b60003083600060058110151561217257fe5b602002015184600160058110151561218657fe5b602002015185600260058110151561219a57fe5b60200201518660036005811015156121ae57fe5b60200201518760046005811015156121c257fe5b60200201518760006006811015156121d657fe5b60200201518860016006811015156121ea57fe5b60200201518960026006811015156121fe57fe5b60200201518a600360068110151561221257fe5b60200201518b600460068110151561222657fe5b60200201518c600560068110151561223a57fe5b6020020151604051808d73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166c010000000000000000000000000281526014018c73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166c010000000000000000000000000281526014018b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166c010000000000000000000000000281526014018a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166c010000000000000000000000000281526014018973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166c010000000000000000000000000281526014018873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166c010000000000000000000000000281526014018781526020018681526020018581526020018481526020018381526020018281526020019c505050505050505050505050506040518091039020905092915050565b61138781565b6040805190810160405280600581526020017f312e302e3000000000000000000000000000000000000000000000000000000081525081565b6000808284029050600084148061246b575082848281151561246857fe5b04145b151561247357fe5b8091505092915050565b600080828481151561248b57fe5b0490508091505092915050565b60008282111515156124a657fe5b818303905092915050565b60008082840190508381101515156124c557fe5b8091505092915050565b60008183106124de57816124e0565b825b905092915050565b600080600080600080600080600033975061250c8a8c60c001518d60a00151611849565b9650600073ffffffffffffffffffffffffffffffffffffffff168b6080015173ffffffffffffffffffffffffffffffffffffffff161415156127b9576000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168b6040015173ffffffffffffffffffffffffffffffffffffffff161495506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168b6060015173ffffffffffffffffffffffffffffffffffffffff161494506126078a8c60c001518d60e00151611849565b935061261d8a8c60c001518d6101000151611849565b92508561262a5783612635565b61263487856124b1565b5b915084612642578261264d565b61264c8a846124b1565b5b90508161267f6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff168d600001516129ac565b10806126b85750816126b66000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff168d60000151612a94565b105b806126ec5750806126ea6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff168a6129ac565b105b8061272057508061271e6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff168a612a94565b105b1561272e576000985061282a565b8515801561276757508661274a8c604001518d600001516129ac565b10806127665750866127648c604001518d60000151612a94565b105b5b15612775576000985061282a565b841580156127a657508961278d8c606001518a6129ac565b10806127a55750896127a38c606001518a612a94565b105b5b156127b4576000985061282a565b612825565b866127cc8c604001518d600001516129ac565b10806127e85750866127e68c604001518d60000151612a94565b105b806127ff5750896127fd8c606001518a6129ac565b105b806128165750896128148c606001518a612a94565b105b15612824576000985061282a565b5b600198505b505050505050505092915050565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166315dacbea868686866040518563ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001828152602001945050505050602060405180830381600087803b15801561296757600080fd5b505af115801561297b573d6000803e3d6000fd5b505050506040513d602081101561299157600080fd5b81019080805190602001909291905050509050949350505050565b60008273ffffffffffffffffffffffffffffffffffffffff166370a0823161138761ffff16846040518363ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001915050602060405180830381600088803b158015612a5057600080fd5b5087f1158015612a64573d6000803e3d6000fd5b50505050506040513d6020811015612a7b57600080fd5b8101908080519060200190929190505050905092915050565b60008273ffffffffffffffffffffffffffffffffffffffff1663dd62ed3e61138761ffff1684600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518463ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200192505050602060405180830381600088803b158015612b8e57600080fd5b5087f1158015612ba2573d6000803e3d6000fd5b50505050506040513d6020811015612bb957600080fd5b8101908080519060200190929190505050905092915050565b61016060405190810160405280600073ffffffffffffffffffffffffffffffffffffffff168152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001600073ffffffffffffffffffffffffffffffffffffffff16815260200160008152602001600081526020016000815260200160008152602001600081526020016000801916815250905600a165627a7a72305820f91599ebd80f85632ef190bb5e1a738e7288d68a2cf9dcc6b579d76b892dcf6f0029'; + '608060405234801561001057600080fd5b50604051604080612d998339810180604052810190808051906020019092919080519060200190929190505050816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505050612cca806100cf6000396000f3006080604052600436106100fc576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806314df96ee14610101578063288cdc911461015a5780632ac126221461019f578063363349be146101e4578063394c21e7146103f85780633b30ba59146104975780634f150787146104ee578063741bcc931461071b5780637e9abb50146107cf5780638163681e1461081457806398024a8b146108a6578063add1cbc5146108fb578063b7b2c7d614610952578063baa0181d14610b8b578063bc61394a14610cef578063cfc4d0ec14610dc3578063f06bbf7514610e60578063ffa1ad7414610e93575b600080fd5b34801561010d57600080fd5b50610140600480360381019080803590602001909291908035906020019092919080359060200190929190505050610f23565b604051808215151515815260200191505060405180910390f35b34801561016657600080fd5b506101896004803603810190808035600019169060200190929190505050610f7b565b6040518082815260200191505060405180910390f35b3480156101ab57600080fd5b506101ce6004803603810190808035600019169060200190929190505050610f93565b6040518082815260200191505060405180910390f35b3480156101f057600080fd5b506103e260048036038101908080359060200190820180359060200190808060200260200160405190810160405280939291908181526020016000905b8282101561027257848483905060a00201600580602002604051908101604052809291908260056020028082843782019150505050508152602001906001019061022d565b5050505050919291929080359060200190820180359060200190808060200260200160405190810160405280939291908181526020016000905b828210156102f157848483905060c0020160068060200260405190810160405280929190826006602002808284378201915050505050815260200190600101906102ac565b5050505050919291929080359060200190929190803515159060200190929190803590602001908201803590602001908080602002602001604051908101604052809392919081815260200183836020028082843782019150505050505091929192908035906020019082018035906020019080806020026020016040519081016040528093929190818152602001838360200280828437820191505050505050919291929080359060200190820180359060200190808060200260200160405190810160405280939291908181526020018383602002808284378201915050505050509192919290505050610fab565b6040518082815260200191505060405180910390f35b34801561040457600080fd5b506104816004803603810190808060a001906005806020026040519081016040528092919082600560200280828437820191505050505091929192908060c0019060068060200260405190810160405280929190826006602002808284378201915050505050919291929080359060200190929190505050611110565b6040518082815260200191505060405180910390f35b3480156104a357600080fd5b506104ac6115f8565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156104fa57600080fd5b5061071960048036038101908080359060200190820180359060200190808060200260200160405190810160405280939291908181526020016000905b8282101561057c57848483905060a002016005806020026040519081016040528092919082600560200280828437820191505050505081526020019060010190610537565b5050505050919291929080359060200190820180359060200190808060200260200160405190810160405280939291908181526020016000905b828210156105fb57848483905060c0020160068060200260405190810160405280929190826006602002808284378201915050505050815260200190600101906105b6565b505050505091929192908035906020019082018035906020019080806020026020016040519081016040528093929190818152602001838360200280828437820191505050505050919291929080359060200190820180359060200190808060200260200160405190810160405280939291908181526020018383602002808284378201915050505050509192919290803590602001908201803590602001908080602002602001604051908101604052809392919081815260200183836020028082843782019150505050505091929192908035906020019082018035906020019080806020026020016040519081016040528093929190818152602001838360200280828437820191505050505050919291929050505061161d565b005b34801561072757600080fd5b506107cd6004803603810190808060a001906005806020026040519081016040528092919082600560200280828437820191505050505091929192908060c0019060068060200260405190810160405280929190826006602002808284378201915050505050919291929080359060200190929190803560ff169060200190929190803560001916906020019092919080356000191690602001909291905050506116da565b005b3480156107db57600080fd5b506107fe60048036038101908080356000191690602001909291905050506116ff565b6040518082815260200191505060405180910390f35b34801561082057600080fd5b5061088c600480360381019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035600019169060200190929190803560ff16906020019092919080356000191690602001909291908035600019169060200190929190505050611748565b604051808215151515815260200191505060405180910390f35b3480156108b257600080fd5b506108e5600480360381019080803590602001909291908035906020019092919080359060200190929190505050611849565b6040518082815260200191505060405180910390f35b34801561090757600080fd5b50610910611867565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561095e57600080fd5b50610b8960048036038101908080359060200190820180359060200190808060200260200160405190810160405280939291908181526020016000905b828210156109e057848483905060a00201600580602002604051908101604052809291908260056020028082843782019150505050508152602001906001019061099b565b5050505050919291929080359060200190820180359060200190808060200260200160405190810160405280939291908181526020016000905b82821015610a5f57848483905060c002016006806020026040519081016040528092919082600660200280828437820191505050505081526020019060010190610a1a565b505050505091929192908035906020019082018035906020019080806020026020016040519081016040528093929190818152602001838360200280828437820191505050505050919291929080351515906020019092919080359060200190820180359060200190808060200260200160405190810160405280939291908181526020018383602002808284378201915050505050509192919290803590602001908201803590602001908080602002602001604051908101604052809392919081815260200183836020028082843782019150505050505091929192908035906020019082018035906020019080806020026020016040519081016040528093929190818152602001838360200280828437820191505050505050919291929050505061188d565b005b348015610b9757600080fd5b50610ced60048036038101908080359060200190820180359060200190808060200260200160405190810160405280939291908181526020016000905b82821015610c1957848483905060a002016005806020026040519081016040528092919082600560200280828437820191505050505081526020019060010190610bd4565b5050505050919291929080359060200190820180359060200190808060200260200160405190810160405280939291908181526020016000905b82821015610c9857848483905060c002016006806020026040519081016040528092919082600660200280828437820191505050505081526020019060010190610c53565b505050505091929192908035906020019082018035906020019080806020026020016040519081016040528093929190818152602001838360200280828437820191505050505050919291929050505061194d565b005b348015610cfb57600080fd5b50610dad6004803603810190808060a001906005806020026040519081016040528092919082600560200280828437820191505050505091929192908060c0019060068060200260405190810160405280929190826006602002808284378201915050505050919291929080359060200190929190803515159060200190929190803560ff169060200190929190803560001916906020019092919080356000191690602001909291905050506119c0565b6040518082815260200191505060405180910390f35b348015610dcf57600080fd5b50610e426004803603810190808060a001906005806020026040519081016040528092919082600560200280828437820191505050505091929192908060c00190600680602002604051908101604052809291908260066020028082843782019150505050509192919290505050612160565b60405180826000191660001916815260200191505060405180910390f35b348015610e6c57600080fd5b50610e7561240b565b604051808261ffff1661ffff16815260200191505060405180910390f35b348015610e9f57600080fd5b50610ea8612411565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015610ee8578082015181840152602081019050610ecd565b50505050905090810190601f168015610f155780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b600080600084801515610f3257fe5b86850991506000821415610f495760009250610f72565b610f68610f5983620f424061244a565b610f63888761244a565b61247d565b90506103e8811192505b50509392505050565b60026020528060005260406000206000915090505481565b60036020528060005260406000206000915090505481565b6000806000809150600090505b895181101561110057896000815181101515610fd057fe5b906020019060200201516003600581101515610fe857fe5b602002015173ffffffffffffffffffffffffffffffffffffffff168a8281518110151561101157fe5b90602001906020020151600360058110151561102957fe5b602002015173ffffffffffffffffffffffffffffffffffffffff1614151561105057600080fd5b6110e4826110df8c8481518110151561106557fe5b906020019060200201518c8581518110151561107d57fe5b906020019060200201516110918d88612498565b8c8c888151811015156110a057fe5b906020019060200201518c898151811015156110b857fe5b906020019060200201518c8a8151811015156110d057fe5b906020019060200201516119c0565b6124b1565b9150878214156110f357611100565b8080600101915050610fb8565b8192505050979650505050505050565b600061111a612bd2565b6000806101606040519081016040528088600060058110151561113957fe5b602002015173ffffffffffffffffffffffffffffffffffffffff16815260200188600160058110151561116857fe5b602002015173ffffffffffffffffffffffffffffffffffffffff16815260200188600260058110151561119757fe5b602002015173ffffffffffffffffffffffffffffffffffffffff1681526020018860036005811015156111c657fe5b602002015173ffffffffffffffffffffffffffffffffffffffff1681526020018860046005811015156111f557fe5b602002015173ffffffffffffffffffffffffffffffffffffffff16815260200187600060068110151561122457fe5b6020020151815260200187600160068110151561123d57fe5b6020020151815260200187600260068110151561125657fe5b6020020151815260200187600360068110151561126f57fe5b6020020151815260200187600460068110151561128857fe5b6020020151815260200161129c8989612160565b6000191681525092503373ffffffffffffffffffffffffffffffffffffffff16836000015173ffffffffffffffffffffffffffffffffffffffff161415156112e357600080fd5b60008360a001511180156112fb575060008360c00151115b80156113075750600085115b151561131257600080fd5b8261012001514210151561136f57826101400151600019166000600381111561133757fe5b60ff167f36d86c59e00bd73dc19ba3adfe068e4b64ac7e92be35546adeddf1b956a87e9060405160405180910390a3600093506115ee565b61138a8360c001516113858561014001516116ff565b612498565b915061139685836124cf565b905060008114156113f05782610140015160001916600160038111156113b857fe5b60ff167f36d86c59e00bd73dc19ba3adfe068e4b64ac7e92be35546adeddf1b956a87e9060405160405180910390a3600093506115ee565b61141a600360008561014001516000191660001916815260200190815260200160002054826124b1565b60036000856101400151600019166000191681526020019081526020016000208190555082604001518360600151604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166c010000000000000000000000000281526014018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166c0100000000000000000000000002815260140192505050604051809103902060001916836080015173ffffffffffffffffffffffffffffffffffffffff16846000015173ffffffffffffffffffffffffffffffffffffffff167f67d66f160bc93d925d05dae1794c90d2d6d6688b29b84ff069398a9b0458713186604001518760600151611552878a60c001518b60a00151611849565b878a6101400151604051808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200184815260200183815260200182600019166000191681526020019550505050505060405180910390a48093505b5050509392505050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008090505b86518110156116d1576116c4878281518110151561163d57fe5b90602001906020020151878381518110151561165557fe5b90602001906020020151878481518110151561166d57fe5b90602001906020020151878581518110151561168557fe5b90602001906020020151878681518110151561169d57fe5b9060200190602002015187878151811015156116b557fe5b906020019060200201516116da565b8080600101915050611623565b50505050505050565b836116eb87878760008888886119c0565b1415156116f757600080fd5b505050505050565b600061174160026000846000191660001916815260200190815260200160002054600360008560001916600019168152602001908152602001600020546124b1565b9050919050565b600060018560405180807f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250601c0182600019166000191681526020019150506040518091039020858585604051600081526020016040526040518085600019166000191681526020018460ff1660ff1681526020018360001916600019168152602001826000191660001916815260200194505050505060206040516020810390808403906000865af1158015611806573d6000803e3d6000fd5b5050506020604051035173ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1614905095945050505050565b600061185e611858858461244a565b8461247d565b90509392505050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008090505b87518110156119435761193588828151811015156118ad57fe5b9060200190602002015188838151811015156118c557fe5b9060200190602002015188848151811015156118dd57fe5b906020019060200201518888868151811015156118f657fe5b90602001906020020151888781518110151561190e57fe5b90602001906020020151888881518110151561192657fe5b906020019060200201516119c0565b508080600101915050611893565b5050505050505050565b60008090505b83518110156119ba576119ac848281518110151561196d57fe5b90602001906020020151848381518110151561198557fe5b90602001906020020151848481518110151561199d57fe5b90602001906020020151611110565b508080600101915050611953565b50505050565b60006119ca612bd2565b600080600080610160604051908101604052808e60006005811015156119ec57fe5b602002015173ffffffffffffffffffffffffffffffffffffffff1681526020018e6001600581101515611a1b57fe5b602002015173ffffffffffffffffffffffffffffffffffffffff1681526020018e6002600581101515611a4a57fe5b602002015173ffffffffffffffffffffffffffffffffffffffff1681526020018e6003600581101515611a7957fe5b602002015173ffffffffffffffffffffffffffffffffffffffff1681526020018e6004600581101515611aa857fe5b602002015173ffffffffffffffffffffffffffffffffffffffff1681526020018d6000600681101515611ad757fe5b602002015181526020018d6001600681101515611af057fe5b602002015181526020018d6002600681101515611b0957fe5b602002015181526020018d6003600681101515611b2257fe5b602002015181526020018d6004600681101515611b3b57fe5b60200201518152602001611b4f8f8f612160565b600019168152509450600073ffffffffffffffffffffffffffffffffffffffff16856020015173ffffffffffffffffffffffffffffffffffffffff161480611bc657503373ffffffffffffffffffffffffffffffffffffffff16856020015173ffffffffffffffffffffffffffffffffffffffff16145b1515611bd157600080fd5b60008560a00151118015611be9575060008560c00151115b8015611bf5575060008b115b1515611c0057600080fd5b611c1685600001518661014001518b8b8b611748565b1515611c2157600080fd5b84610120015142101515611c7e578461014001516000191660006003811115611c4657fe5b60ff167f36d86c59e00bd73dc19ba3adfe068e4b64ac7e92be35546adeddf1b956a87e9060405160405180910390a360009550612150565b611c998560c00151611c948761014001516116ff565b612498565b9350611ca58b856124cf565b95506000861415611cff578461014001516000191660016003811115611cc757fe5b60ff167f36d86c59e00bd73dc19ba3adfe068e4b64ac7e92be35546adeddf1b956a87e9060405160405180910390a360009550612150565b611d12868660c001518760a00151610f23565b15611d66578461014001516000191660026003811115611d2e57fe5b60ff167f36d86c59e00bd73dc19ba3adfe068e4b64ac7e92be35546adeddf1b956a87e9060405160405180910390a360009550612150565b89158015611d7b5750611d7985876124e8565b155b15611dce5784610140015160001916600380811115611d9657fe5b60ff167f36d86c59e00bd73dc19ba3adfe068e4b64ac7e92be35546adeddf1b956a87e9060405160405180910390a360009550612150565b611de1868660c001518760a00151611849565b9250611e0d600260008761014001516000191660001916815260200190815260200160002054876124b1565b600260008761014001516000191660001916815260200190815260200160002081905550611e45856040015186600001513386612838565b1515611e5057600080fd5b611e64856060015133876000015189612838565b1515611e6f57600080fd5b600073ffffffffffffffffffffffffffffffffffffffff16856080015173ffffffffffffffffffffffffffffffffffffffff16141515611f6e5760008560e001511115611f0c57611ec9868660c001518760e00151611849565b9150611f006000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff168660000151876080015185612838565b1515611f0b57600080fd5b5b60008561010001511115611f6d57611f2e868660c00151876101000151611849565b9050611f616000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1633876080015184612838565b1515611f6c57600080fd5b5b5b84604001518560600151604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166c010000000000000000000000000281526014018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166c0100000000000000000000000002815260140192505050604051809103902060001916856080015173ffffffffffffffffffffffffffffffffffffffff16866000015173ffffffffffffffffffffffffffffffffffffffff167f0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb33389604001518a60600151898d8a8a8f6101400151604051808973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200186815260200185815260200184815260200183815260200182600019166000191681526020019850505050505050505060405180910390a48595505b5050505050979650505050505050565b60003083600060058110151561217257fe5b602002015184600160058110151561218657fe5b602002015185600260058110151561219a57fe5b60200201518660036005811015156121ae57fe5b60200201518760046005811015156121c257fe5b60200201518760006006811015156121d657fe5b60200201518860016006811015156121ea57fe5b60200201518960026006811015156121fe57fe5b60200201518a600360068110151561221257fe5b60200201518b600460068110151561222657fe5b60200201518c600560068110151561223a57fe5b6020020151604051808d73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166c010000000000000000000000000281526014018c73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166c010000000000000000000000000281526014018b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166c010000000000000000000000000281526014018a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166c010000000000000000000000000281526014018973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166c010000000000000000000000000281526014018873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166c010000000000000000000000000281526014018781526020018681526020018581526020018481526020018381526020018281526020019c505050505050505050505050506040518091039020905092915050565b61138781565b6040805190810160405280600581526020017f312e302e3000000000000000000000000000000000000000000000000000000081525081565b6000808284029050600084148061246b575082848281151561246857fe5b04145b151561247357fe5b8091505092915050565b600080828481151561248b57fe5b0490508091505092915050565b60008282111515156124a657fe5b818303905092915050565b60008082840190508381101515156124c557fe5b8091505092915050565b60008183106124de57816124e0565b825b905092915050565b600080600080600080600080600033975061250c8a8c60c001518d60a00151611849565b9650600073ffffffffffffffffffffffffffffffffffffffff168b6080015173ffffffffffffffffffffffffffffffffffffffff161415156127b9576000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168b6040015173ffffffffffffffffffffffffffffffffffffffff161495506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168b6060015173ffffffffffffffffffffffffffffffffffffffff161494506126078a8c60c001518d60e00151611849565b935061261d8a8c60c001518d6101000151611849565b92508561262a5783612635565b61263487856124b1565b5b915084612642578261264d565b61264c8a846124b1565b5b90508161267f6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff168d600001516129ac565b10806126b85750816126b66000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff168d60000151612a94565b105b806126ec5750806126ea6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff168a6129ac565b105b8061272057508061271e6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff168a612a94565b105b1561272e576000985061282a565b8515801561276757508661274a8c604001518d600001516129ac565b10806127665750866127648c604001518d60000151612a94565b105b5b15612775576000985061282a565b841580156127a657508961278d8c606001518a6129ac565b10806127a55750896127a38c606001518a612a94565b105b5b156127b4576000985061282a565b612825565b866127cc8c604001518d600001516129ac565b10806127e85750866127e68c604001518d60000151612a94565b105b806127ff5750896127fd8c606001518a6129ac565b105b806128165750896128148c606001518a612a94565b105b15612824576000985061282a565b5b600198505b505050505050505092915050565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166315dacbea868686866040518563ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001828152602001945050505050602060405180830381600087803b15801561296757600080fd5b505af115801561297b573d6000803e3d6000fd5b505050506040513d602081101561299157600080fd5b81019080805190602001909291905050509050949350505050565b60008273ffffffffffffffffffffffffffffffffffffffff166370a0823161138761ffff16846040518363ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001915050602060405180830381600088803b158015612a5057600080fd5b5087f1158015612a64573d6000803e3d6000fd5b50505050506040513d6020811015612a7b57600080fd5b8101908080519060200190929190505050905092915050565b60008273ffffffffffffffffffffffffffffffffffffffff1663dd62ed3e61138761ffff1684600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518463ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200192505050602060405180830381600088803b158015612b8e57600080fd5b5087f1158015612ba2573d6000803e3d6000fd5b50505050506040513d6020811015612bb957600080fd5b8101908080519060200190929190505050905092915050565b61016060405190810160405280600073ffffffffffffffffffffffffffffffffffffffff168152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001600073ffffffffffffffffffffffffffffffffffffffff16815260200160008152602001600081526020016000815260200160008152602001600081526020016000801916815250905600a165627a7a72305820f91599ebd80f85632ef190bb5e1a738e7288d68a2cf9dcc6b579d76b892dcf6f0029'; diff --git a/packages/deployer/test/util/constants.ts b/packages/deployer/test/util/constants.ts index b93081a80..88d6db550 100644 --- a/packages/deployer/test/util/constants.ts +++ b/packages/deployer/test/util/constants.ts @@ -7,5 +7,5 @@ export const constants = { timeoutMs: 30000, zrxTokenAddress: '0xe41d2489571d322189246dafa5ebde1f4699f498', tokenTransferProxyAddress: '0x8da0d80f5007ef1e431dd2127178d224e32c2ef4', - specifiedContracts: '*', + contracts: '*' as '*', }; diff --git a/packages/dev-utils/src/coverage.ts b/packages/dev-utils/src/coverage.ts index 67c87ec30..743573874 100644 --- a/packages/dev-utils/src/coverage.ts +++ b/packages/dev-utils/src/coverage.ts @@ -13,7 +13,7 @@ export const coverage = { return coverageSubprovider; }, _getCoverageSubprovider(): CoverageSubprovider { - const artifactsPath = '../migrations/src/artifacts'; + const artifactsPath = '../migrations/artifacts/1.0.0'; const contractsPath = 'src/contracts'; const networkId = 50; const defaultFromAddress = constants.TESTRPC_FIRST_ADDRESS; diff --git a/packages/metacoin/compiler.json b/packages/metacoin/compiler.json new file mode 100644 index 000000000..f5817e0c8 --- /dev/null +++ b/packages/metacoin/compiler.json @@ -0,0 +1,15 @@ +{ + "compilerSettings": { + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode.object", + "evm.bytecode.sourceMap", + "evm.deployedBytecode.object", + "evm.deployedBytecode.sourceMap" + ] + } + } + } +} diff --git a/packages/metacoin/package.json b/packages/metacoin/package.json index 1ab749017..17aad0aaf 100644 --- a/packages/metacoin/package.json +++ b/packages/metacoin/package.json @@ -12,13 +12,13 @@ "build": "tsc", "test": "run-s build run_mocha", "test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov", - "run_mocha": "mocha lib/test/**/*.js --bail --exit", + "run_mocha": "mocha lib/test/**/*_test.js lib/test/global_hooks.js --bail --exit", "generate_contract_wrappers": "node ../abi-gen/lib/index.js --abis 'artifacts/Metacoin.json' --template ../contract_templates/contract.handlebars --partials '../contract_templates/partials/**/*.handlebars' --output src/contract_wrappers --backend ethers && prettier --write 'src/contract_wrappers/**.ts'", "coverage:report:text": "istanbul report text", "coverage:report:html": "istanbul report html && open coverage/index.html", "coverage:report:lcov": "istanbul report lcov", "test:circleci": "yarn test:coverage", - "compile": "node ../deployer/lib/src/cli.js compile --contracts Metacoin --contracts-dir contracts --artifacts-dir artifacts" + "compile": "node ../deployer/lib/src/cli.js compile" }, "author": "", "license": "Apache-2.0", diff --git a/packages/migrations/artifacts/1.0.0/.gitkeep b/packages/migrations/artifacts/1.0.0/.gitkeep new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/packages/migrations/artifacts/1.0.0/.gitkeep diff --git a/packages/sol-cov/compiler.json b/packages/sol-cov/compiler.json new file mode 100644 index 000000000..a6a0c6d3a --- /dev/null +++ b/packages/sol-cov/compiler.json @@ -0,0 +1,18 @@ +{ + "contracts": ["SimpleStorage"], + "contractsDir": "test/fixtures/contracts", + "artifactsDir": "test/fixtures/artifacts", + "compilerSettings": { + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode.object", + "evm.bytecode.sourceMap", + "evm.deployedBytecode.object", + "evm.deployedBytecode.sourceMap" + ] + } + } + } +} diff --git a/packages/sol-cov/package.json b/packages/sol-cov/package.json index 4bea117de..536d05370 100644 --- a/packages/sol-cov/package.json +++ b/packages/sol-cov/package.json @@ -14,7 +14,7 @@ "run_mocha": "mocha lib/test/**/*_test.js --exit", "clean": "shx rm -rf lib scripts", "build": "copyfiles 'test/fixtures/**/*' ./lib && tsc && copyfiles -u 3 './lib/src/monorepo_scripts/**/*' ./scripts", - "compile_test": "node ../deployer/lib/src/cli.js compile --contracts SimpleStorage --contracts-dir test/fixtures/contracts --artifacts-dir test/fixtures/artifacts", + "compile_test": "node ../deployer/lib/src/cli.js compile", "manual:postpublish": "yarn build; node ./scripts/postpublish.js", "docs:stage": "yarn build && node ./scripts/stage_docs.js", "docs:json": "typedoc --excludePrivate --excludeExternals --target ES5 --json $JSON_FILE_PATH $PROJECT_FILES", diff --git a/packages/sol-cov/src/collect_contract_data.ts b/packages/sol-cov/src/collect_contract_data.ts index 1d8bc7178..bb20e98be 100644 --- a/packages/sol-cov/src/collect_contract_data.ts +++ b/packages/sol-cov/src/collect_contract_data.ts @@ -8,25 +8,24 @@ import { ContractData } from './types'; export const collectContractsData = (artifactsPath: string, sourcesPath: string, networkId: number) => { const artifactsGlob = `${artifactsPath}/**/*.json`; const artifactFileNames = glob.sync(artifactsGlob, { absolute: true }); - const contractsDataIfExists: Array<ContractData | {}> = _.map(artifactFileNames, artifactFileName => { + const contractsData: ContractData[] = []; + _.forEach(artifactFileNames, artifactFileName => { const artifact = JSON.parse(fs.readFileSync(artifactFileName).toString()); - const sources = artifact.networks[networkId].sources; - const contractName = artifact.contract_name; + const sources = _.keys(artifact.sources); + const contractName = artifact.contractName; // We don't compute coverage for dependencies - const sourceCodes = _.map(sources, (source: string) => fs.readFileSync(source).toString()); - if (_.isUndefined(artifact.networks[networkId])) { - throw new Error(`No ${contractName} artifacts found for networkId ${networkId}`); - } + const sourceCodes = _.map(sources, (source: string) => + fs.readFileSync(path.join(sourcesPath, source)).toString(), + ); const contractData = { sourceCodes, sources, - sourceMap: artifact.networks[networkId].source_map, - sourceMapRuntime: artifact.networks[networkId].source_map_runtime, - runtimeBytecode: artifact.networks[networkId].runtime_bytecode, - bytecode: artifact.networks[networkId].bytecode, + bytecode: artifact.compilerOutput.evm.bytecode.object, + sourceMap: artifact.compilerOutput.evm.bytecode.sourceMap, + runtimeBytecode: artifact.compilerOutput.evm.deployedBytecode.object, + sourceMapRuntime: artifact.compilerOutput.evm.deployedBytecode.sourceMap, }; - return contractData; + contractsData.push(contractData); }); - const contractsData = _.filter(contractsDataIfExists, contractData => !_.isEmpty(contractData)) as ContractData[]; return contractsData; }; diff --git a/packages/sol-cov/src/coverage_manager.ts b/packages/sol-cov/src/coverage_manager.ts index 230ccc3c9..6a57d07c9 100644 --- a/packages/sol-cov/src/coverage_manager.ts +++ b/packages/sol-cov/src/coverage_manager.ts @@ -29,10 +29,31 @@ import { import { utils } from './utils'; export class CoverageManager { + private _sourcesPath: string; private _traceInfos: TraceInfo[] = []; private _contractsData: ContractData[] = []; private _getContractCodeAsync: (address: string) => Promise<string>; - private static _getSingleFileCoverageForTrace( + constructor( + artifactsPath: string, + sourcesPath: string, + networkId: number, + getContractCodeAsync: (address: string) => Promise<string>, + ) { + this._getContractCodeAsync = getContractCodeAsync; + this._sourcesPath = sourcesPath; + this._contractsData = collectContractsData(artifactsPath, this._sourcesPath, networkId); + } + public appendTraceInfo(traceInfo: TraceInfo): void { + this._traceInfos.push(traceInfo); + } + public async writeCoverageAsync(): Promise<void> { + const finalCoverage = await this._computeCoverageAsync(); + const jsonReplacer: null = null; + const numberOfJsonSpaces = 4; + const stringifiedCoverage = JSON.stringify(finalCoverage, jsonReplacer, numberOfJsonSpaces); + fs.writeFileSync('coverage/coverage.json', stringifiedCoverage); + } + private _getSingleFileCoverageForTrace( contractData: ContractData, coveredPcs: number[], pcToSourceRange: { [programCounter: number]: SourceRange }, @@ -94,11 +115,12 @@ export class CoverageManager { ); statementCoverage[modifierStatementId] = isModifierCovered; } + const absoluteFileName = path.join(this._sourcesPath, fileName); const partialCoverage = { - [contractData.sources[fileIndex]]: { + [absoluteFileName]: { ...coverageEntriesDescription, l: {}, // It's able to derive it from statement coverage - path: fileName, + path: absoluteFileName, f: functionCoverage, s: statementCoverage, b: branchCoverage, @@ -106,31 +128,13 @@ export class CoverageManager { }; return partialCoverage; } - constructor( - artifactsPath: string, - sourcesPath: string, - networkId: number, - getContractCodeAsync: (address: string) => Promise<string>, - ) { - this._getContractCodeAsync = getContractCodeAsync; - this._contractsData = collectContractsData(artifactsPath, sourcesPath, networkId); - } - public appendTraceInfo(traceInfo: TraceInfo): void { - this._traceInfos.push(traceInfo); - } - public async writeCoverageAsync(): Promise<void> { - const finalCoverage = await this._computeCoverageAsync(); - const jsonReplacer: null = null; - const numberOfJsonSpaces = 4; - const stringifiedCoverage = JSON.stringify(finalCoverage, jsonReplacer, numberOfJsonSpaces); - fs.writeFileSync('coverage/coverage.json', stringifiedCoverage); - } private async _computeCoverageAsync(): Promise<Coverage> { const collector = new Collector(); for (const traceInfo of this._traceInfos) { if (traceInfo.address !== constants.NEW_CONTRACT) { // Runtime transaction - const runtimeBytecode = (traceInfo as TraceInfoExistingContract).runtimeBytecode; + let runtimeBytecode = (traceInfo as TraceInfoExistingContract).runtimeBytecode; + runtimeBytecode = utils.removeHexPrefix(runtimeBytecode); const contractData = _.find(this._contractsData, { runtimeBytecode }) as ContractData; if (_.isUndefined(contractData)) { throw new Error(`Transaction to an unknown address: ${traceInfo.address}`); @@ -144,7 +148,7 @@ export class CoverageManager { contractData.sources, ); for (let fileIndex = 0; fileIndex < contractData.sources.length; fileIndex++) { - const singleFileCoverageForTrace = CoverageManager._getSingleFileCoverageForTrace( + const singleFileCoverageForTrace = this._getSingleFileCoverageForTrace( contractData, traceInfo.coveredPcs, pcToSourceRange, @@ -154,7 +158,8 @@ export class CoverageManager { } } else { // Contract creation transaction - const bytecode = (traceInfo as TraceInfoNewContract).bytecode; + let bytecode = (traceInfo as TraceInfoNewContract).bytecode; + bytecode = utils.removeHexPrefix(bytecode); const contractData = _.find(this._contractsData, contractDataCandidate => bytecode.startsWith(contractDataCandidate.bytecode), ) as ContractData; @@ -170,7 +175,7 @@ export class CoverageManager { contractData.sources, ); for (let fileIndex = 0; fileIndex < contractData.sources.length; fileIndex++) { - const singleFileCoverageForTrace = CoverageManager._getSingleFileCoverageForTrace( + const singleFileCoverageForTrace = this._getSingleFileCoverageForTrace( contractData, traceInfo.coveredPcs, pcToSourceRange, diff --git a/packages/sol-cov/src/utils.ts b/packages/sol-cov/src/utils.ts index f155043a1..d970c42ee 100644 --- a/packages/sol-cov/src/utils.ts +++ b/packages/sol-cov/src/utils.ts @@ -4,6 +4,10 @@ export const utils = { compareLineColumn(lhs: LineColumn, rhs: LineColumn): number { return lhs.line !== rhs.line ? lhs.line - rhs.line : lhs.column - rhs.column; }, + removeHexPrefix(hex: string): string { + const hexPrefix = '0x'; + return hex.startsWith(hexPrefix) ? hex.slice(hexPrefix.length) : hex; + }, isRangeInside(childRange: SingleFileSourceRange, parentRange: SingleFileSourceRange): boolean { return ( utils.compareLineColumn(parentRange.start, childRange.start) <= 0 && diff --git a/packages/sol-resolver/src/index.ts b/packages/sol-resolver/src/index.ts index cd6ec42ec..a86053259 100644 --- a/packages/sol-resolver/src/index.ts +++ b/packages/sol-resolver/src/index.ts @@ -3,6 +3,7 @@ export { FallthroughResolver } from './resolvers/fallthrough_resolver'; export { URLResolver } from './resolvers/url_resolver'; export { NPMResolver } from './resolvers/npm_resolver'; export { FSResolver } from './resolvers/fs_resolver'; +export { RelativeFSResolver } from './resolvers/relative_fs_resolver'; export { NameResolver } from './resolvers/name_resolver'; export { EnumerableResolver } from './resolvers/enumerable_resolver'; export { Resolver } from './resolvers/resolver'; diff --git a/packages/sol-resolver/src/resolvers/name_resolver.ts b/packages/sol-resolver/src/resolvers/name_resolver.ts index 6849b7610..76bed802e 100644 --- a/packages/sol-resolver/src/resolvers/name_resolver.ts +++ b/packages/sol-resolver/src/resolvers/name_resolver.ts @@ -18,7 +18,8 @@ export class NameResolver extends EnumerableResolver { const onFile = (filePath: string) => { const contractName = path.basename(filePath, SOLIDITY_FILE_EXTENSION); if (contractName === lookupContractName) { - const source = fs.readFileSync(filePath).toString(); + const absoluteContractPath = path.join(this._contractsDir, filePath); + const source = fs.readFileSync(absoluteContractPath).toString(); contractSource = { source, path: filePath, @@ -35,7 +36,8 @@ export class NameResolver extends EnumerableResolver { const contractSources: ContractSource[] = []; const onFile = (filePath: string) => { const contractName = path.basename(filePath, SOLIDITY_FILE_EXTENSION); - const source = fs.readFileSync(filePath).toString(); + const absoluteContractPath = path.join(this._contractsDir, filePath); + const source = fs.readFileSync(absoluteContractPath).toString(); const contractSource = { source, path: filePath, @@ -54,9 +56,10 @@ export class NameResolver extends EnumerableResolver { throw new Error(`No directory found at ${dirPath}`); } for (const fileName of dirContents) { - const entryPath = path.join(dirPath, fileName); - const isDirectory = fs.lstatSync(entryPath).isDirectory(); - const isComplete = isDirectory ? this._traverseContractsDir(entryPath, onFile) : onFile(entryPath); + const absoluteEntryPath = path.join(dirPath, fileName); + const isDirectory = fs.lstatSync(absoluteEntryPath).isDirectory(); + const entryPath = path.relative(this._contractsDir, absoluteEntryPath); + const isComplete = isDirectory ? this._traverseContractsDir(absoluteEntryPath, onFile) : onFile(entryPath); if (isComplete) { return isComplete; } diff --git a/packages/sol-resolver/src/resolvers/relative_fs_resolver.ts b/packages/sol-resolver/src/resolvers/relative_fs_resolver.ts new file mode 100644 index 000000000..2317ede83 --- /dev/null +++ b/packages/sol-resolver/src/resolvers/relative_fs_resolver.ts @@ -0,0 +1,26 @@ +import * as fs from 'fs'; +import * as path from 'path'; + +import { ContractSource } from '../types'; + +import { Resolver } from './resolver'; + +export class RelativeFSResolver extends Resolver { + private _contractsDir: string; + constructor(contractsDir: string) { + super(); + this._contractsDir = contractsDir; + } + // tslint:disable-next-line:prefer-function-over-method + public resolveIfExists(importPath: string): ContractSource | undefined { + const filePath = path.join(this._contractsDir, importPath); + if (fs.existsSync(filePath)) { + const fileContent = fs.readFileSync(filePath).toString(); + return { + source: fileContent, + path: importPath, + }; + } + return undefined; + } +} diff --git a/packages/typescript-typings/types/solc/index.d.ts b/packages/typescript-typings/types/solc/index.d.ts index b20f9b2ff..5a87ac267 100644 --- a/packages/typescript-typings/types/solc/index.d.ts +++ b/packages/typescript-typings/types/solc/index.d.ts @@ -59,32 +59,33 @@ declare module 'solc' { | 'evm.gasEstimates' | 'ewasm.wast' | 'ewasm.wasm'; + export interface CompilerSettings { + remappings?: string[]; + optimizer?: { + enabled: boolean; + runs?: number; + }; + evmVersion?: 'homestead' | 'tangerineWhistle' | 'spuriousDragon' | 'byzantium' | 'constantinople'; + metadata?: { + useLiteralContent: true; + }; + libraries?: { + [fileName: string]: { + [libName: string]: string; + }; + }; + outputSelection: { + [fileName: string]: { + [contractName: string]: OutputField[]; + }; + }; + } export interface StandardInput { language: 'Solidity' | 'serpent' | 'lll' | 'assembly'; sources: { [fileName: string]: Source; }; - settings: { - remappings?: string[]; - optimizer?: { - enabled: boolean; - runs?: number; - }; - evmVersion?: 'homestead' | 'tangerineWhistle' | 'spuriousDragon' | 'byzantium' | 'constantinople'; - metadata?: { - useLiteralContent: true; - }; - libraries?: { - [fileName: string]: { - [libName: string]: string; - }; - }; - outputSelection: { - [fileName: string]: { - [contractName: string]: OutputField[]; - }; - }; - }; + settings: CompilerSettings; } export type ErrorType = | 'JSONError' @@ -114,6 +115,19 @@ declare module 'solc' { formattedMessage?: string; } import { ContractAbi } from '@0xproject/types'; + export interface StandardContractOutput { + abi: ContractAbi; + evm: { + bytecode: { + object: string; + sourceMap: string; + }; + deployedBytecode: { + object: string; + sourceMap: string; + }; + }; + } export interface StandardOutput { errors: Error[]; sources: { @@ -125,19 +139,7 @@ declare module 'solc' { }; contracts: { [fileName: string]: { - [contractName: string]: { - abi: ContractAbi; - evm: { - bytecode: { - object: string; - sourceMap: string; - }; - deployedBytecode: { - object: string; - sourceMap: string; - }; - }; - }; + [contractName: string]: StandardContractOutput; }; }; } diff --git a/packages/website/public/images/landing/project_logos/the_ocean.png b/packages/website/public/images/landing/project_logos/the_ocean.png Binary files differindex 74678b5d4..0677abc29 100644 --- a/packages/website/public/images/landing/project_logos/the_ocean.png +++ b/packages/website/public/images/landing/project_logos/the_ocean.png diff --git a/packages/website/ts/components/legacy_portal/legacy_portal.tsx b/packages/website/ts/components/legacy_portal/legacy_portal.tsx index 8942e4356..c45b20365 100644 --- a/packages/website/ts/components/legacy_portal/legacy_portal.tsx +++ b/packages/website/ts/components/legacy_portal/legacy_portal.tsx @@ -154,7 +154,6 @@ export class LegacyPortal extends React.Component<LegacyPortalProps, LegacyPorta const updateShouldBlockchainErrDialogBeOpen = this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen.bind( this.props.dispatcher, ); - const isDevelopment = configs.ENVIRONMENT === Environments.DEVELOPMENT; const portalStyle: React.CSSProperties = { minHeight: '100vh', display: 'flex', @@ -204,18 +203,6 @@ export class LegacyPortal extends React.Component<LegacyPortalProps, LegacyPorta <div className="py2" style={{ backgroundColor: colors.grey50 }}> {this.props.blockchainIsLoaded ? ( <Switch> - {isDevelopment && ( - <Route - path={`${WebsitePaths.Portal}/wallet`} - render={this._renderWallet.bind(this)} - /> - )} - {isDevelopment && ( - <Route - path={`${WebsitePaths.Portal}/relayers`} - render={this._renderRelayers.bind(this)} - /> - )} <Route path={`${WebsitePaths.Portal}/weth`} render={this._renderEthWrapper.bind(this)} @@ -294,40 +281,6 @@ export class LegacyPortal extends React.Component<LegacyPortalProps, LegacyPorta isLedgerDialogOpen: !this.state.isLedgerDialogOpen, }); } - private _renderWallet() { - const allTokens = _.values(this.props.tokenByAddress); - const trackedTokens = _.filter(allTokens, t => t.isTracked); - return ( - <div className="flex flex-center"> - <div className="mx-auto"> - <Wallet - userAddress={this.props.userAddress} - networkId={this.props.networkId} - blockchain={this._blockchain} - blockchainIsLoaded={this.props.blockchainIsLoaded} - blockchainErr={this.props.blockchainErr} - dispatcher={this.props.dispatcher} - tokenByAddress={this.props.tokenByAddress} - trackedTokens={trackedTokens} - userEtherBalanceInWei={this.props.userEtherBalanceInWei} - lastForceTokenStateRefetch={this.props.lastForceTokenStateRefetch} - injectedProviderName={this.props.injectedProviderName} - providerType={this.props.providerType} - onToggleLedgerDialog={this.onToggleLedgerDialog.bind(this)} - /> - </div> - </div> - ); - } - private _renderRelayers() { - return ( - <div className="flex flex-center"> - <div className="mx-auto" style={{ width: 800 }}> - <RelayerIndex networkId={this.props.networkId} /> - </div> - </div> - ); - } private _renderEthWrapper() { return ( <EthWrappers diff --git a/packages/website/ts/components/legacy_portal/legacy_portal_menu.tsx b/packages/website/ts/components/legacy_portal/legacy_portal_menu.tsx index 634d966ed..94113f066 100644 --- a/packages/website/ts/components/legacy_portal/legacy_portal_menu.tsx +++ b/packages/website/ts/components/legacy_portal/legacy_portal_menu.tsx @@ -58,26 +58,6 @@ export class LegacyPortalMenu extends React.Component<LegacyPortalMenuProps, Leg > {this._renderMenuItemWithIcon('Wrap ETH', 'zmdi-circle-o')} </MenuItem> - {configs.ENVIRONMENT === Environments.DEVELOPMENT && ( - <div> - <MenuItem - style={this.props.menuItemStyle} - className="py2" - to={`${WebsitePaths.Portal}/wallet`} - onClick={this.props.onClick.bind(this)} - > - {this._renderMenuItemWithIcon('Wallet', 'zmdi-balance-wallet')} - </MenuItem> - <MenuItem - style={this.props.menuItemStyle} - className="py2" - to={`${WebsitePaths.Portal}/relayers`} - onClick={this.props.onClick.bind(this)} - > - {this._renderMenuItemWithIcon('Relayers', 'zmdi-input-antenna')} - </MenuItem> - </div> - )} </div> ); } diff --git a/packages/website/ts/components/portal/portal.tsx b/packages/website/ts/components/portal/portal.tsx new file mode 100644 index 000000000..b5e8150c4 --- /dev/null +++ b/packages/website/ts/components/portal/portal.tsx @@ -0,0 +1,263 @@ +import { colors, Styles } from '@0xproject/react-shared'; +import { BigNumber } from '@0xproject/utils'; +import * as _ from 'lodash'; +import * as React from 'react'; +import * as DocumentTitle from 'react-document-title'; + +import { Blockchain } from 'ts/blockchain'; +import { BlockchainErrDialog } from 'ts/components/dialogs/blockchain_err_dialog'; +import { LedgerConfigDialog } from 'ts/components/dialogs/ledger_config_dialog'; +import { PortalDisclaimerDialog } from 'ts/components/dialogs/portal_disclaimer_dialog'; +import { AssetPicker } from 'ts/components/generate_order/asset_picker'; +import { RelayerIndex } from 'ts/components/relayer_index/relayer_index'; +import { TopBar, TopBarDisplayType } from 'ts/components/top_bar/top_bar'; +import { FlashMessage } from 'ts/components/ui/flash_message'; +import { Wallet } from 'ts/components/wallet/wallet'; +import { localStorage } from 'ts/local_storage/local_storage'; +import { Dispatcher } from 'ts/redux/dispatcher'; +import { BlockchainErrs, HashData, Order, ProviderType, ScreenWidths, TokenByAddress, TokenVisibility } from 'ts/types'; +import { constants } from 'ts/utils/constants'; +import { Translate } from 'ts/utils/translate'; +import { utils } from 'ts/utils/utils'; + +export interface PortalProps { + blockchainErr: BlockchainErrs; + blockchainIsLoaded: boolean; + dispatcher: Dispatcher; + hashData: HashData; + injectedProviderName: string; + networkId: number; + nodeVersion: string; + orderFillAmount: BigNumber; + providerType: ProviderType; + screenWidth: ScreenWidths; + tokenByAddress: TokenByAddress; + userEtherBalanceInWei: BigNumber; + userAddress: string; + shouldBlockchainErrDialogBeOpen: boolean; + userSuppliedOrderCache: Order; + location: Location; + flashMessage?: string | React.ReactNode; + lastForceTokenStateRefetch: number; + translate: Translate; +} + +interface PortalState { + prevNetworkId: number; + prevNodeVersion: string; + prevUserAddress: string; + prevPathname: string; + isDisclaimerDialogOpen: boolean; + isLedgerDialogOpen: boolean; + isAssetPickerDialogOpen: boolean; +} + +const THROTTLE_TIMEOUT = 100; +const TOP_BAR_HEIGHT = TopBar.heightForDisplayType(TopBarDisplayType.Expanded); + +const styles: Styles = { + root: { + width: '100%', + height: '100%', + backgroundColor: colors.lightestGrey, + }, + body: { + height: `calc(100vh - ${TOP_BAR_HEIGHT}px)`, + }, + scrollContainer: { + height: `calc(100vh - ${TOP_BAR_HEIGHT}px)`, + WebkitOverflowScrolling: 'touch', + overflow: 'auto', + }, + title: { + fontWeight: 'bold', + fontSize: 20, + }, +}; + +export class Portal extends React.Component<PortalProps, PortalState> { + private _blockchain: Blockchain; + private _throttledScreenWidthUpdate: () => void; + constructor(props: PortalProps) { + super(props); + this._throttledScreenWidthUpdate = _.throttle(this._updateScreenWidth.bind(this), THROTTLE_TIMEOUT); + const didAcceptPortalDisclaimer = localStorage.getItemIfExists(constants.LOCAL_STORAGE_KEY_ACCEPT_DISCLAIMER); + const hasAcceptedDisclaimer = + !_.isUndefined(didAcceptPortalDisclaimer) && !_.isEmpty(didAcceptPortalDisclaimer); + this.state = { + prevNetworkId: this.props.networkId, + prevNodeVersion: this.props.nodeVersion, + prevUserAddress: this.props.userAddress, + prevPathname: this.props.location.pathname, + isDisclaimerDialogOpen: !hasAcceptedDisclaimer, + isAssetPickerDialogOpen: false, + isLedgerDialogOpen: false, + }; + } + public componentDidMount() { + window.addEventListener('resize', this._throttledScreenWidthUpdate); + window.scrollTo(0, 0); + } + public componentWillMount() { + this._blockchain = new Blockchain(this.props.dispatcher); + } + public componentWillUnmount() { + this._blockchain.destroy(); + window.removeEventListener('resize', this._throttledScreenWidthUpdate); + // We re-set the entire redux state when the portal is unmounted so that when it is re-rendered + // the initialization process always occurs from the same base state. This helps avoid + // initialization inconsistencies (i.e While the portal was unrendered, the user might have + // become disconnected from their backing Ethereum node, changed user accounts, etc...) + this.props.dispatcher.resetState(); + } + public componentWillReceiveProps(nextProps: PortalProps) { + if (nextProps.networkId !== this.state.prevNetworkId) { + // tslint:disable-next-line:no-floating-promises + this._blockchain.networkIdUpdatedFireAndForgetAsync(nextProps.networkId); + this.setState({ + prevNetworkId: nextProps.networkId, + }); + } + if (nextProps.userAddress !== this.state.prevUserAddress) { + const newUserAddress = _.isEmpty(nextProps.userAddress) ? undefined : nextProps.userAddress; + // tslint:disable-next-line:no-floating-promises + this._blockchain.userAddressUpdatedFireAndForgetAsync(newUserAddress); + this.setState({ + prevUserAddress: nextProps.userAddress, + }); + } + if (nextProps.nodeVersion !== this.state.prevNodeVersion) { + // tslint:disable-next-line:no-floating-promises + this._blockchain.nodeVersionUpdatedFireAndForgetAsync(nextProps.nodeVersion); + } + if (nextProps.location.pathname !== this.state.prevPathname) { + this.setState({ + prevPathname: nextProps.location.pathname, + }); + } + } + public render() { + const updateShouldBlockchainErrDialogBeOpen = this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen.bind( + this.props.dispatcher, + ); + const allTokens = _.values(this.props.tokenByAddress); + const trackedTokens = _.filter(allTokens, t => t.isTracked); + return ( + <div style={styles.root}> + <DocumentTitle title="0x Portal DApp" /> + <TopBar + userAddress={this.props.userAddress} + networkId={this.props.networkId} + injectedProviderName={this.props.injectedProviderName} + onToggleLedgerDialog={this._onToggleLedgerDialog.bind(this)} + dispatcher={this.props.dispatcher} + providerType={this.props.providerType} + blockchainIsLoaded={this.props.blockchainIsLoaded} + location={this.props.location} + blockchain={this._blockchain} + translate={this.props.translate} + displayType={TopBarDisplayType.Expanded} + style={{ backgroundColor: colors.lightestGrey }} + /> + <div id="portal" style={styles.body}> + <div className="sm-flex flex-center"> + <div className="flex-last px3"> + <div className="py3" style={styles.title}> + Your Account + </div> + <Wallet + userAddress={this.props.userAddress} + networkId={this.props.networkId} + blockchain={this._blockchain} + blockchainIsLoaded={this.props.blockchainIsLoaded} + blockchainErr={this.props.blockchainErr} + dispatcher={this.props.dispatcher} + tokenByAddress={this.props.tokenByAddress} + trackedTokens={trackedTokens} + userEtherBalanceInWei={this.props.userEtherBalanceInWei} + lastForceTokenStateRefetch={this.props.lastForceTokenStateRefetch} + injectedProviderName={this.props.injectedProviderName} + providerType={this.props.providerType} + onToggleLedgerDialog={this._onToggleLedgerDialog.bind(this)} + onAddToken={this._onAddToken.bind(this)} + /> + </div> + <div className="flex-auto px3" style={styles.scrollContainer}> + <div className="py3" style={styles.title}> + Explore 0x Ecosystem + </div> + <RelayerIndex networkId={this.props.networkId} /> + </div> + </div> + <BlockchainErrDialog + blockchain={this._blockchain} + blockchainErr={this.props.blockchainErr} + isOpen={this.props.shouldBlockchainErrDialogBeOpen} + userAddress={this.props.userAddress} + toggleDialogFn={updateShouldBlockchainErrDialogBeOpen} + networkId={this.props.networkId} + /> + <PortalDisclaimerDialog + isOpen={this.state.isDisclaimerDialogOpen} + onToggleDialog={this._onPortalDisclaimerAccepted.bind(this)} + /> + <FlashMessage dispatcher={this.props.dispatcher} flashMessage={this.props.flashMessage} /> + {this.props.blockchainIsLoaded && ( + <LedgerConfigDialog + providerType={this.props.providerType} + networkId={this.props.networkId} + blockchain={this._blockchain} + dispatcher={this.props.dispatcher} + toggleDialogFn={this._onToggleLedgerDialog.bind(this)} + isOpen={this.state.isLedgerDialogOpen} + /> + )} + <AssetPicker + userAddress={this.props.userAddress} + networkId={this.props.networkId} + blockchain={this._blockchain} + dispatcher={this.props.dispatcher} + isOpen={this.state.isAssetPickerDialogOpen} + currentTokenAddress={''} + onTokenChosen={this._onTokenChosen.bind(this)} + tokenByAddress={this.props.tokenByAddress} + tokenVisibility={TokenVisibility.UNTRACKED} + /> + </div> + </div> + ); + } + private _onTokenChosen(tokenAddress: string) { + if (_.isEmpty(tokenAddress)) { + this.setState({ + isAssetPickerDialogOpen: false, + }); + return; + } + const token = this.props.tokenByAddress[tokenAddress]; + this.props.dispatcher.updateTokenByAddress([token]); + this.setState({ + isAssetPickerDialogOpen: false, + }); + } + private _onToggleLedgerDialog() { + this.setState({ + isLedgerDialogOpen: !this.state.isLedgerDialogOpen, + }); + } + private _onAddToken() { + this.setState({ + isAssetPickerDialogOpen: !this.state.isAssetPickerDialogOpen, + }); + } + private _onPortalDisclaimerAccepted() { + localStorage.setItem(constants.LOCAL_STORAGE_KEY_ACCEPT_DISCLAIMER, 'set'); + this.setState({ + isDisclaimerDialogOpen: false, + }); + } + private _updateScreenWidth() { + const newScreenWidth = utils.getScreenWidth(); + this.props.dispatcher.updateScreenWidth(newScreenWidth); + } +} diff --git a/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx b/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx index 0b9b8165e..d88a59d15 100644 --- a/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx +++ b/packages/website/ts/components/relayer_index/relayer_grid_tile.tsx @@ -12,37 +12,6 @@ export interface RelayerGridTileProps { networkId: number; } -// TODO: Get top tokens from remote -const topTokens = [ - { - address: '0x1dad4783cf3fe3085c1426157ab175a6119a04ba', - decimals: 18, - iconUrl: '/images/token_icons/makerdao.png', - isRegistered: true, - isTracked: true, - name: 'Maker DAO', - symbol: 'MKR', - }, - { - address: '0x323b5d4c32345ced77393b3530b1eed0f346429d', - decimals: 18, - iconUrl: '/images/token_icons/melon.png', - isRegistered: true, - isTracked: true, - name: 'Melon Token', - symbol: 'MLN', - }, - { - address: '0xb18845c260f680d5b9d84649638813e342e4f8c9', - decimals: 18, - iconUrl: '/images/token_icons/augur.png', - isRegistered: true, - isTracked: true, - name: 'Augur Reputation Token', - symbol: 'REP', - }, -]; - const styles: Styles = { root: { backgroundColor: colors.white, @@ -93,28 +62,63 @@ const styles: Styles = { }, }; +const FALLBACK_IMG_SRC = '/images/landing/hero_chip_image.png'; + export const RelayerGridTile: React.StatelessComponent<RelayerGridTileProps> = (props: RelayerGridTileProps) => { const link = props.relayerInfo.appUrl || props.relayerInfo.url; return ( - <a href={link} target="_blank" style={{ textDecoration: 'none' }}> - <GridTile style={styles.root}> - <div style={styles.innerDiv}> - <img src={props.relayerInfo.headerImgUrl} style={styles.header} /> - <div style={styles.body}> - <div className="py1" style={styles.relayerNameLabel}> - {props.relayerInfo.name} - </div> - <div style={styles.dailyTradeVolumeLabel}>{props.relayerInfo.dailyTxnVolume}</div> - <div className="py1" style={styles.subLabel}> - Daily Trade Volume - </div> - <TopTokens tokens={topTokens} networkId={props.networkId} /> - <div className="py1" style={styles.subLabel}> - Top tokens - </div> + <GridTile style={styles.root}> + <div style={styles.innerDiv}> + <a href={link} target="_blank" style={{ textDecoration: 'none' }}> + <ImgWithFallback + src={props.relayerInfo.headerImgUrl} + fallbackSrc={FALLBACK_IMG_SRC} + style={styles.header} + /> + </a> + <div style={styles.body}> + <div className="py1" style={styles.relayerNameLabel}> + {props.relayerInfo.name} + </div> + <div style={styles.dailyTradeVolumeLabel}>{props.relayerInfo.dailyTxnVolume}</div> + <div className="py1" style={styles.subLabel}> + Daily Trade Volume + </div> + <TopTokens tokens={props.relayerInfo.topTokens} networkId={props.networkId} /> + <div className="py1" style={styles.subLabel}> + Top tokens </div> </div> - </GridTile> - </a> + </div> + </GridTile> ); }; + +interface ImgWithFallbackProps { + src?: string; + fallbackSrc: string; + style: React.CSSProperties; +} +interface ImgWithFallbackState { + imageLoadFailed: boolean; +} +class ImgWithFallback extends React.Component<ImgWithFallbackProps, ImgWithFallbackState> { + constructor(props: ImgWithFallbackProps) { + super(props); + this.state = { + imageLoadFailed: false, + }; + } + public render() { + if (this.state.imageLoadFailed || _.isUndefined(this.props.src)) { + return <img src={this.props.fallbackSrc} style={this.props.style} />; + } else { + return <img src={this.props.src} onError={this._onError.bind(this)} style={this.props.style} />; + } + } + private _onError() { + this.setState({ + imageLoadFailed: true, + }); + } +} diff --git a/packages/website/ts/components/relayer_index/relayer_index.tsx b/packages/website/ts/components/relayer_index/relayer_index.tsx index c1ab4227a..dffd0f83f 100644 --- a/packages/website/ts/components/relayer_index/relayer_index.tsx +++ b/packages/website/ts/components/relayer_index/relayer_index.tsx @@ -1,5 +1,7 @@ import { colors, Styles } from '@0xproject/react-shared'; import * as _ from 'lodash'; +import CircularProgress from 'material-ui/CircularProgress'; +import FlatButton from 'material-ui/FlatButton'; import { GridList } from 'material-ui/GridList'; import * as React from 'react'; @@ -32,9 +34,9 @@ const styles: Styles = { }, }; -const CELL_HEIGHT = 260; +const CELL_HEIGHT = 290; const NUMBER_OF_COLUMNS = 4; -const GRID_PADDING = 16; +const GRID_PADDING = 20; export class RelayerIndex extends React.Component<RelayerIndexProps, RelayerIndexState> { private _isUnmounted: boolean; @@ -55,7 +57,24 @@ export class RelayerIndex extends React.Component<RelayerIndexProps, RelayerInde } public render() { const readyToRender = _.isUndefined(this.state.error) && !_.isUndefined(this.state.relayerInfos); - if (readyToRender) { + if (!readyToRender) { + return ( + <div className="col col-12" style={{ ...styles.root, height: '100%' }}> + <div + className="relative sm-px2 sm-pt2 sm-m1" + style={{ height: 122, top: '33%', transform: 'translateY(-50%)' }} + > + <div className="center pb2"> + {_.isUndefined(this.state.error) ? ( + <CircularProgress size={40} thickness={5} /> + ) : ( + <Retry onRetry={this._fetchRelayerInfosAsync.bind(this)} /> + )} + </div> + </div> + </div> + ); + } else { return ( <div style={styles.root}> <GridList @@ -64,23 +83,22 @@ export class RelayerIndex extends React.Component<RelayerIndexProps, RelayerInde padding={GRID_PADDING} style={styles.gridList} > - {this.state.relayerInfos.map((relayerInfo: WebsiteBackendRelayerInfo) => ( - <RelayerGridTile - key={relayerInfo.name} - relayerInfo={relayerInfo} - networkId={this.props.networkId} - /> + {this.state.relayerInfos.map((relayerInfo: WebsiteBackendRelayerInfo, index) => ( + <RelayerGridTile key={index} relayerInfo={relayerInfo} networkId={this.props.networkId} /> ))} </GridList> </div> ); - } else { - // TODO: loading and error states with a scrolling container - return null; } } private async _fetchRelayerInfosAsync(): Promise<void> { try { + if (!this._isUnmounted) { + this.setState({ + relayerInfos: undefined, + error: undefined, + }); + } const relayerInfos = await backendClient.getRelayerInfosAsync(); if (!this._isUnmounted) { this.setState({ @@ -96,3 +114,31 @@ export class RelayerIndex extends React.Component<RelayerIndexProps, RelayerInde } } } + +interface RetryProps { + onRetry: () => void; +} +const Retry = (props: RetryProps) => ( + <div className="clearfix center" style={{ color: colors.black }}> + <div className="mx-auto inline-block align-middle" style={{ lineHeight: '44px', textAlign: 'center' }}> + <div className="h2" style={{ fontFamily: 'Roboto Mono' }}> + Something went wrong. + </div> + <div className="py3"> + <FlatButton + label={'reload'} + backgroundColor={colors.black} + labelStyle={{ + fontSize: 18, + fontFamily: 'Roboto Mono', + fontWeight: 'lighter', + color: colors.white, + textTransform: 'lowercase', + }} + style={{ width: 280, height: 62, borderRadius: 5 }} + onClick={props.onRetry} + /> + </div> + </div> + </div> +); diff --git a/packages/website/ts/components/relayer_index/relayer_top_tokens.tsx b/packages/website/ts/components/relayer_index/relayer_top_tokens.tsx index 233590b78..db4d3a211 100644 --- a/packages/website/ts/components/relayer_index/relayer_top_tokens.tsx +++ b/packages/website/ts/components/relayer_index/relayer_top_tokens.tsx @@ -3,10 +3,10 @@ import * as _ from 'lodash'; import * as React from 'react'; import { TokenIcon } from 'ts/components/ui/token_icon'; -import { Token } from 'ts/types'; +import { WebsiteBackendTokenInfo } from 'ts/types'; export interface TopTokensProps { - tokens: Token[]; + tokens: WebsiteBackendTokenInfo[]; networkId: number; } @@ -23,17 +23,17 @@ const styles: Styles = { export const TopTokens: React.StatelessComponent<TopTokensProps> = (props: TopTokensProps) => { return ( <div className="flex"> - {_.map(props.tokens, (token: Token, index: number) => { + {_.map(props.tokens, (tokenInfo: WebsiteBackendTokenInfo, index: number) => { const firstItemStyle = { ...styles.tokenLabel, ...styles.followingTokenLabel }; const style = index !== 0 ? firstItemStyle : styles.tokenLabel; return ( <a - key={token.address} - href={tokenLinkFromToken(token, props.networkId)} + key={tokenInfo.address} + href={tokenLinkFromToken(tokenInfo, props.networkId)} target="_blank" style={style} > - {token.symbol} + {tokenInfo.symbol} </a> ); })} @@ -41,6 +41,6 @@ export const TopTokens: React.StatelessComponent<TopTokensProps> = (props: TopTo ); }; -function tokenLinkFromToken(token: Token, networkId: number) { - return sharedUtils.getEtherScanLinkIfExists(token.address, networkId, EtherscanLinkSuffixes.Address); +function tokenLinkFromToken(tokenInfo: WebsiteBackendTokenInfo, networkId: number) { + return sharedUtils.getEtherScanLinkIfExists(tokenInfo.address, networkId, EtherscanLinkSuffixes.Address); } diff --git a/packages/website/ts/components/top_bar/provider_display.tsx b/packages/website/ts/components/top_bar/provider_display.tsx index 221c34f8c..4cd50dba9 100644 --- a/packages/website/ts/components/top_bar/provider_display.tsx +++ b/packages/website/ts/components/top_bar/provider_display.tsx @@ -1,4 +1,4 @@ -import { colors } from '@0xproject/react-shared'; +import { colors, Styles } from '@0xproject/react-shared'; import * as _ from 'lodash'; import RaisedButton from 'material-ui/RaisedButton'; import * as React from 'react'; @@ -11,9 +11,9 @@ import { ProviderType } from 'ts/types'; import { constants } from 'ts/utils/constants'; import { utils } from 'ts/utils/utils'; -const IDENTICON_DIAMETER = 32; +const ROOT_HEIGHT = 24; -interface ProviderDisplayProps { +export interface ProviderDisplayProps { dispatcher: Dispatcher; userAddress: string; networkId: number; @@ -25,6 +25,15 @@ interface ProviderDisplayProps { interface ProviderDisplayState {} +const styles: Styles = { + root: { + height: ROOT_HEIGHT, + backgroundColor: colors.white, + borderRadius: ROOT_HEIGHT, + boxShadow: `0px 4px 6px ${colors.walletBoxShadow}`, + }, +}; + export class ProviderDisplay extends React.Component<ProviderDisplayProps, ProviderDisplayState> { public render() { const isAddressAvailable = !_.isEmpty(this.props.userAddress); @@ -40,21 +49,20 @@ export class ProviderDisplay extends React.Component<ProviderDisplayProps, Provi : 'Connect a wallet'; const providerTitle = this.props.providerType === ProviderType.Injected ? injectedProviderName : 'Ledger Nano S'; + const isProviderMetamask = providerTitle === constants.PROVIDER_NAME_METAMASK; const hoverActiveNode = ( - <div className="flex right lg-pr0 md-pr2 sm-pr2" style={{ paddingTop: 16 }}> + <div className="flex right lg-pr0 md-pr2 sm-pr2 p1" style={styles.root}> <div> - <Identicon address={this.props.userAddress} diameter={IDENTICON_DIAMETER} /> - </div> - <div style={{ marginLeft: 12, paddingTop: 1 }}> - <div style={{ fontSize: 12, color: colors.amber800 }}>{providerTitle}</div> - <div style={{ fontSize: 14 }}>{displayAddress}</div> + <Identicon address={this.props.userAddress} diameter={ROOT_HEIGHT} /> </div> - <div - style={{ borderLeft: `1px solid ${colors.grey300}`, marginLeft: 17, paddingTop: 1 }} - className="px2" - > - <i style={{ fontSize: 30, color: colors.grey300 }} className="zmdi zmdi zmdi-chevron-down" /> + <div style={{ marginLeft: 12, paddingTop: 3 }}> + <div style={{ fontSize: 16, color: colors.darkGrey }}>{displayAddress}</div> </div> + {isProviderMetamask && ( + <div style={{ marginLeft: 16 }}> + <img src="/images/metamask_icon.png" style={{ width: ROOT_HEIGHT, height: ROOT_HEIGHT }} /> + </div> + )} </div> ); const hasInjectedProvider = diff --git a/packages/website/ts/components/top_bar/top_bar.tsx b/packages/website/ts/components/top_bar/top_bar.tsx index 7fb476ba0..2502bea6d 100644 --- a/packages/website/ts/components/top_bar/top_bar.tsx +++ b/packages/website/ts/components/top_bar/top_bar.tsx @@ -19,7 +19,12 @@ import { Deco, Key, ProviderType, WebsiteLegacyPaths, WebsitePaths } from 'ts/ty import { constants } from 'ts/utils/constants'; import { Translate } from 'ts/utils/translate'; -interface TopBarProps { +export enum TopBarDisplayType { + Default, + Expanded, +} + +export interface TopBarProps { userAddress?: string; networkId?: number; injectedProviderName?: string; @@ -34,7 +39,7 @@ interface TopBarProps { availableDocVersions?: string[]; menu?: DocsMenu; menuSubsectionsBySection?: MenuSubsectionsBySection; - shouldFullWidth?: boolean; + displayType?: TopBarDisplayType; docsInfo?: DocsInfo; style?: React.CSSProperties; isNightVersion?: boolean; @@ -47,17 +52,8 @@ interface TopBarState { } const styles: Styles = { - address: { - marginRight: 12, - overflow: 'hidden', - paddingTop: 4, - textOverflow: 'ellipsis', - whiteSpace: 'nowrap', - width: 70, - }, topBar: { backgroundColor: colors.white, - height: 59, width: '100%', position: 'relative', top: 0, @@ -78,12 +74,19 @@ const styles: Styles = { }, }; +const DEFAULT_HEIGHT = 59; +const EXPANDED_HEIGHT = 75; + export class TopBar extends React.Component<TopBarProps, TopBarState> { public static defaultProps: Partial<TopBarProps> = { - shouldFullWidth: false, + displayType: TopBarDisplayType.Default, style: {}, isNightVersion: false, }; + public static heightForDisplayType(displayType: TopBarDisplayType) { + const result = displayType === TopBarDisplayType.Expanded ? EXPANDED_HEIGHT : DEFAULT_HEIGHT; + return result + 1; + } constructor(props: TopBarProps) { super(props); this.state = { @@ -92,8 +95,9 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> { } public render() { const isNightVersion = this.props.isNightVersion; - const isFullWidthPage = this.props.shouldFullWidth; - const parentClassNames = `flex mx-auto ${isFullWidthPage ? 'pl2' : 'max-width-4'}`; + const isExpandedDisplayType = this.props.displayType === TopBarDisplayType.Expanded; + const parentClassNames = `flex mx-auto ${isExpandedDisplayType ? 'pl3 py1' : 'max-width-4'}`; + const height = isExpandedDisplayType ? EXPANDED_HEIGHT : DEFAULT_HEIGHT; const developerSectionMenuItems = [ <Link key="subMenuItem-zeroEx" to={WebsitePaths.ZeroExJs} className="text-decoration-none"> <MenuItem style={{ fontSize: styles.menuItem.fontSize }} primaryText="0x.js" /> @@ -178,9 +182,11 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> { </a>, ]; const bottomBorderStyle = this._shouldDisplayBottomBar() ? styles.bottomBar : {}; - const fullWidthClasses = isFullWidthPage ? 'pr4' : ''; + const fullWidthClasses = isExpandedDisplayType ? 'pr4' : ''; const logoUrl = isNightVersion ? '/images/protocol_logo_white.png' : '/images/protocol_logo_black.png'; - const menuClasses = `col col-${isFullWidthPage ? '4' : '5'} ${fullWidthClasses} lg-pr0 md-pr2 sm-hide xs-hide`; + const menuClasses = `col col-${ + isExpandedDisplayType ? '4' : '5' + } ${fullWidthClasses} lg-pr0 md-pr2 sm-hide xs-hide`; const menuIconStyle = { fontSize: 25, color: isNightVersion ? 'white' : 'black', @@ -197,15 +203,15 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> { ); const popoverContent = <Menu style={{ color: colors.darkGrey }}>{developerSectionMenuItems}</Menu>; return ( - <div style={{ ...styles.topBar, ...bottomBorderStyle, ...this.props.style }} className="pb1"> + <div style={{ ...styles.topBar, ...bottomBorderStyle, ...this.props.style, ...{ height } }} className="pb1"> <div className={parentClassNames}> <div className="col col-2 sm-pl1 md-pl2 lg-pl0" style={{ paddingTop: 15 }}> <Link to={`${WebsitePaths.Home}`} className="text-decoration-none"> <img src={logoUrl} height="30" /> </Link> </div> - <div className={`col col-${isFullWidthPage ? '8' : '9'} lg-hide md-hide`} /> - <div className={`col col-${isFullWidthPage ? '6' : '5'} sm-hide xs-hide`} /> + <div className={`col col-${isExpandedDisplayType ? '8' : '9'} lg-hide md-hide`} /> + <div className={`col col-${isExpandedDisplayType ? '6' : '5'} sm-hide xs-hide`} /> {!this._isViewingPortal() && ( <div className={menuClasses}> <div className="flex justify-between"> @@ -242,7 +248,7 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> { path={`${WebsitePaths.Portal}`} isPrimary={true} style={styles.menuItem} - className={`${isFullWidthPage && 'md-hide'}`} + className={`${isExpandedDisplayType && 'md-hide'}`} isNightVersion={isNightVersion} isExternal={false} /> @@ -250,7 +256,7 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> { </div> )} {this.props.blockchainIsLoaded && ( - <div className="sm-hide xs-hide col col-5"> + <div className="sm-hide xs-hide col col-5" style={{ paddingTop: 8, marginRight: 36 }}> <ProviderDisplay dispatcher={this.props.dispatcher} userAddress={this.props.userAddress} @@ -262,7 +268,7 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> { /> </div> )} - <div className={`col ${isFullWidthPage ? 'col-2 pl2' : 'col-1'} md-hide lg-hide`}> + <div className={`col ${isExpandedDisplayType ? 'col-2 pl2' : 'col-1'} md-hide lg-hide`}> <div style={menuIconStyle}> <i className="zmdi zmdi-menu" onClick={this._onMenuButtonClick.bind(this)} /> </div> @@ -441,21 +447,6 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> { </div> ); } - private _renderUser() { - const userAddress = this.props.userAddress; - const identiconDiameter = 26; - return ( - <div className="flex right lg-pr0 md-pr2 sm-pr2" style={{ paddingTop: 16 }}> - <div style={styles.address} data-tip={true} data-for="userAddressTooltip"> - {!_.isEmpty(userAddress) ? userAddress : ''} - </div> - <ReactTooltip id="userAddressTooltip">{userAddress}</ReactTooltip> - <div> - <Identicon address={userAddress} diameter={identiconDiameter} /> - </div> - </div> - ); - } private _onMenuButtonClick() { this.setState({ isDrawerOpen: !this.state.isDrawerOpen, @@ -511,7 +502,8 @@ export class TopBar extends React.Component<TopBarProps, TopBarState> { this._isViewingJsonSchemasDocs() || this._isViewingSolCovDocs() || this._isViewingSubprovidersDocs() || - this._isViewingConnectDocs() + this._isViewingConnectDocs() || + this._isViewingPortal() ); } } // tslint:disable:max-file-line-count diff --git a/packages/website/ts/components/wallet/wallet.tsx b/packages/website/ts/components/wallet/wallet.tsx index 057c712e5..a28012aaf 100644 --- a/packages/website/ts/components/wallet/wallet.tsx +++ b/packages/website/ts/components/wallet/wallet.tsx @@ -55,11 +55,13 @@ export interface WalletProps { injectedProviderName: string; providerType: ProviderType; onToggleLedgerDialog: () => void; + onAddToken: () => void; } interface WalletState { trackedTokenStateByAddress: TokenStateByAddress; wrappedEtherDirection?: Side; + isHoveringSidebar: boolean; } interface AllowanceToggleConfig { @@ -94,6 +96,9 @@ const styles: Styles = { }, footerItemInnerDiv: { paddingLeft: 24, + borderTopColor: colors.walletBorder, + borderTopStyle: 'solid', + borderWidth: 1, }, borderedItem: { borderBottomColor: colors.walletBorder, @@ -114,7 +119,17 @@ const styles: Styles = { paddingTop: 8, paddingBottom: 8, }, - accessoryItemsContainer: { width: 150, right: 8 }, + accessoryItemsContainer: { + width: 150, + right: 8, + }, + bodyInnerDiv: { + padding: 0, + // TODO: make this completely responsive + maxHeight: 475, + overflow: 'auto', + WebkitOverflowScrolling: 'touch', + }, }; const ETHER_ICON_PATH = '/images/ether.png'; @@ -139,6 +154,7 @@ export class Wallet extends React.Component<WalletProps, WalletState> { this.state = { trackedTokenStateByAddress: initialTrackedTokenStateByAddress, wrappedEtherDirection: undefined, + isHoveringSidebar: false, }; } public componentWillMount() { @@ -184,12 +200,7 @@ export class Wallet extends React.Component<WalletProps, WalletState> { return ( <List style={styles.list}> {isAddressAvailable - ? _.concat( - this._renderConnectedHeaderRows(), - this._renderEthRows(), - this._renderTokenRows(), - this._renderFooterRows(), - ) + ? _.concat(this._renderConnectedHeaderRows(), this._renderBody(), this._renderFooterRows()) : _.concat(this._renderDisconnectedHeaderRows(), this._renderDisconnectedRows())} </List> ); @@ -230,9 +241,43 @@ export class Wallet extends React.Component<WalletProps, WalletState> { /> ); } + private _renderBody() { + const bodyStyle: React.CSSProperties = { + ...styles.bodyInnerDiv, + overflow: this.state.isHoveringSidebar ? 'auto' : 'hidden', + }; + return ( + <ListItem + key="body" + innerDivStyle={bodyStyle} + onMouseEnter={this._onSidebarHover.bind(this)} + onMouseLeave={this._onSidebarHoverOff.bind(this)} + > + {this._renderEthRows()} + {this._renderTokenRows()} + </ListItem> + ); + } + private _onSidebarHover(event: React.FormEvent<HTMLInputElement>) { + this.setState({ + isHoveringSidebar: true, + }); + } + private _onSidebarHoverOff() { + this.setState({ + isHoveringSidebar: false, + }); + } private _renderFooterRows() { const primaryText = '+ other tokens'; - return <ListItem key={FOOTER_ITEM_KEY} primaryText={primaryText} innerDivStyle={styles.footerItemInnerDiv} />; + return ( + <ListItem + key={FOOTER_ITEM_KEY} + primaryText={primaryText} + innerDivStyle={styles.footerItemInnerDiv} + onClick={this.props.onAddToken} + /> + ); } private _renderEthRows() { const primaryText = this._renderAmount( @@ -293,7 +338,7 @@ export class Wallet extends React.Component<WalletProps, WalletState> { ); return _.map(trackedTokensStartingWithEtherToken, this._renderTokenRow.bind(this)); } - private _renderTokenRow(token: Token) { + private _renderTokenRow(token: Token, index: number) { const tokenState = this.state.trackedTokenStateByAddress[token.address]; const tokenLink = sharedUtils.getEtherScanLinkIfExists( token.address, @@ -310,12 +355,14 @@ export class Wallet extends React.Component<WalletProps, WalletState> { tokenState, }, }; + // if this is the last item in the list, do not render the border, it is rendered by the footer + const borderedStyle = index !== this.props.trackedTokens.length - 1 ? styles.borderedItem : {}; const shouldShowWrapEtherItem = !_.isUndefined(this.state.wrappedEtherDirection) && this.state.wrappedEtherDirection === accessoryItemConfig.wrappedEtherDirection; const style = shouldShowWrapEtherItem ? { ...walletItemStyles.focusedItem, ...styles.paddedItem } - : { ...styles.tokenItem, ...styles.borderedItem, ...styles.paddedItem }; + : { ...styles.tokenItem, ...borderedStyle, ...styles.paddedItem }; const etherToken = this._getEthToken(); return ( <div key={token.address}> diff --git a/packages/website/ts/containers/portal.ts b/packages/website/ts/containers/portal.ts new file mode 100644 index 000000000..3f0feb6e9 --- /dev/null +++ b/packages/website/ts/containers/portal.ts @@ -0,0 +1,88 @@ +import { BigNumber } from '@0xproject/utils'; +import * as _ from 'lodash'; +import * as React from 'react'; +import { connect } from 'react-redux'; +import { Dispatch } from 'redux'; +import { Portal as PortalComponent, PortalProps as PortalComponentProps } from 'ts/components/portal/portal'; +import { Dispatcher } from 'ts/redux/dispatcher'; +import { State } from 'ts/redux/reducer'; +import { BlockchainErrs, HashData, Order, ProviderType, ScreenWidths, Side, TokenByAddress } from 'ts/types'; +import { constants } from 'ts/utils/constants'; +import { Translate } from 'ts/utils/translate'; + +interface ConnectedState { + blockchainErr: BlockchainErrs; + blockchainIsLoaded: boolean; + hashData: HashData; + injectedProviderName: string; + networkId: number; + nodeVersion: string; + orderFillAmount: BigNumber; + providerType: ProviderType; + tokenByAddress: TokenByAddress; + lastForceTokenStateRefetch: number; + userEtherBalanceInWei: BigNumber; + screenWidth: ScreenWidths; + shouldBlockchainErrDialogBeOpen: boolean; + userAddress: string; + userSuppliedOrderCache: Order; + flashMessage?: string | React.ReactNode; + translate: Translate; +} + +interface ConnectedDispatch { + dispatcher: Dispatcher; +} + +const mapStateToProps = (state: State, ownProps: PortalComponentProps): ConnectedState => { + const receiveAssetToken = state.sideToAssetToken[Side.Receive]; + const depositAssetToken = state.sideToAssetToken[Side.Deposit]; + const receiveAddress = !_.isUndefined(receiveAssetToken.address) + ? receiveAssetToken.address + : constants.NULL_ADDRESS; + const depositAddress = !_.isUndefined(depositAssetToken.address) + ? depositAssetToken.address + : constants.NULL_ADDRESS; + const receiveAmount = !_.isUndefined(receiveAssetToken.amount) ? receiveAssetToken.amount : new BigNumber(0); + const depositAmount = !_.isUndefined(depositAssetToken.amount) ? depositAssetToken.amount : new BigNumber(0); + const hashData = { + depositAmount, + depositTokenContractAddr: depositAddress, + feeRecipientAddress: constants.NULL_ADDRESS, + makerFee: constants.MAKER_FEE, + orderExpiryTimestamp: state.orderExpiryTimestamp, + orderMakerAddress: state.userAddress, + orderTakerAddress: state.orderTakerAddress !== '' ? state.orderTakerAddress : constants.NULL_ADDRESS, + receiveAmount, + receiveTokenContractAddr: receiveAddress, + takerFee: constants.TAKER_FEE, + orderSalt: state.orderSalt, + }; + return { + blockchainErr: state.blockchainErr, + blockchainIsLoaded: state.blockchainIsLoaded, + hashData, + injectedProviderName: state.injectedProviderName, + networkId: state.networkId, + nodeVersion: state.nodeVersion, + orderFillAmount: state.orderFillAmount, + providerType: state.providerType, + screenWidth: state.screenWidth, + shouldBlockchainErrDialogBeOpen: state.shouldBlockchainErrDialogBeOpen, + tokenByAddress: state.tokenByAddress, + lastForceTokenStateRefetch: state.lastForceTokenStateRefetch, + userAddress: state.userAddress, + userEtherBalanceInWei: state.userEtherBalanceInWei, + userSuppliedOrderCache: state.userSuppliedOrderCache, + flashMessage: state.flashMessage, + translate: state.translate, + }; +}; + +const mapDispatchToProps = (dispatch: Dispatch<State>): ConnectedDispatch => ({ + dispatcher: new Dispatcher(dispatch), +}); + +export const Portal: React.ComponentClass<PortalComponentProps> = connect(mapStateToProps, mapDispatchToProps)( + PortalComponent, +); diff --git a/packages/website/ts/index.tsx b/packages/website/ts/index.tsx index 9535dd222..1b1255214 100644 --- a/packages/website/ts/index.tsx +++ b/packages/website/ts/index.tsx @@ -34,9 +34,14 @@ import 'less/all.less'; // cause we only want to import the module when the user navigates to the page. // At the same time webpack statically parses for System.import() to determine bundle chunk split points // so each lazy import needs it's own `System.import()` declaration. -const LazyPortal = createLazyComponent('LegacyPortal', async () => - System.import<any>(/* webpackChunkName: "legacyPortal" */ 'ts/containers/legacy_portal'), -); +const LazyPortal = + utils.isDevelopment() || utils.isStaging() + ? createLazyComponent('Portal', async () => + System.import<any>(/* webpackChunkName: "portal" */ 'ts/containers/portal'), + ) + : createLazyComponent('LegacyPortal', async () => + System.import<any>(/* webpackChunkName: "legacyPortal" */ 'ts/containers/legacy_portal'), + ); const LazyZeroExJSDocumentation = createLazyComponent('Documentation', async () => System.import<any>(/* webpackChunkName: "zeroExDocs" */ 'ts/containers/zero_ex_js_documentation'), ); diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts index 2754075a7..67e4a5d7d 100644 --- a/packages/website/ts/types.ts +++ b/packages/website/ts/types.ts @@ -503,19 +503,26 @@ export interface TokenState { price?: BigNumber; } -// TODO: Add topTokens property once available from backend export interface WebsiteBackendRelayerInfo { name: string; dailyTxnVolume: string; url: string; appUrl?: string; - headerImgUrl: string; + headerImgUrl?: string; + topTokens: WebsiteBackendTokenInfo[]; } export interface WebsiteBackendPriceInfo { [symbol: string]: string; } +export interface WebsiteBackendTokenInfo { + address: string; + decimals: number; + name: string; + symbol: string; +} + export interface WebsiteBackendGasInfo { average: number; } |