Updated deployer to accept a list of contract directories as input. Contract directories are namespaced to a void clashes. Also in this commit is a fix for overloading contract functions.
This commit is contained in:
parent
073bf738dd
commit
61fc3346c2
@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { AbiDefinition, ConstructorAbi, EventAbi, MethodAbi } from '@0xproject/types';
|
||||
import { logUtils } from '@0xproject/utils';
|
||||
import { abiUtils, logUtils } from '@0xproject/utils';
|
||||
import chalk from 'chalk';
|
||||
import * as fs from 'fs';
|
||||
import { sync as globSync } from 'glob';
|
||||
@ -12,7 +12,7 @@ import * as yargs from 'yargs';
|
||||
|
||||
import toSnakeCase = require('to-snake-case');
|
||||
|
||||
import { ContextData, ContractsBackend, ParamKind } from './types';
|
||||
import { ContextData, ContractsBackend, Method, ParamKind } from './types';
|
||||
import { utils } from './utils';
|
||||
|
||||
const ABI_TYPE_CONSTRUCTOR = 'constructor';
|
||||
@ -83,7 +83,6 @@ function writeOutputFile(name: string, renderedTsCode: string): void {
|
||||
|
||||
Handlebars.registerHelper('parameterType', utils.solTypeToTsType.bind(utils, ParamKind.Input, args.backend));
|
||||
Handlebars.registerHelper('returnType', utils.solTypeToTsType.bind(utils, ParamKind.Output, args.backend));
|
||||
|
||||
if (args.partials) {
|
||||
registerPartials(args.partials);
|
||||
}
|
||||
@ -126,11 +125,12 @@ for (const abiFileName of abiFileNames) {
|
||||
}
|
||||
|
||||
const methodAbis = ABI.filter((abi: AbiDefinition) => abi.type === ABI_TYPE_METHOD) as MethodAbi[];
|
||||
const methodsData = _.map(methodAbis, methodAbi => {
|
||||
_.map(methodAbi.inputs, (input, i: number) => {
|
||||
const methodAbisSanitized = abiUtils.renameOverloadedMethods(methodAbis) as MethodAbi[];
|
||||
const methodsData = _.map(methodAbis, (methodAbi, methodAbiIndex: number) => {
|
||||
_.forEach(methodAbi.inputs, (input, inputIndex: number) => {
|
||||
if (_.isEmpty(input.name)) {
|
||||
// Auto-generated getters don't have parameter names
|
||||
input.name = `index_${i}`;
|
||||
input.name = `index_${inputIndex}`;
|
||||
}
|
||||
});
|
||||
// This will make templates simpler
|
||||
@ -138,6 +138,8 @@ for (const abiFileName of abiFileNames) {
|
||||
...methodAbi,
|
||||
singleReturnValue: methodAbi.outputs.length === 1,
|
||||
hasReturnValue: methodAbi.outputs.length !== 0,
|
||||
tsName: methodAbisSanitized[methodAbiIndex].name,
|
||||
functionSignature: abiUtils.getFunctionSignature(methodAbi),
|
||||
};
|
||||
return methodData;
|
||||
});
|
||||
|
@ -5,13 +5,6 @@ export enum ParamKind {
|
||||
Output = 'output',
|
||||
}
|
||||
|
||||
export enum AbiType {
|
||||
Function = 'function',
|
||||
Constructor = 'constructor',
|
||||
Event = 'event',
|
||||
Fallback = 'fallback',
|
||||
}
|
||||
|
||||
export enum ContractsBackend {
|
||||
Web3 = 'web3',
|
||||
Ethers = 'ethers',
|
||||
@ -20,6 +13,8 @@ export enum ContractsBackend {
|
||||
export interface Method extends MethodAbi {
|
||||
singleReturnValue: boolean;
|
||||
hasReturnValue: boolean;
|
||||
tsName: string;
|
||||
functionSignature: string;
|
||||
}
|
||||
|
||||
export interface ContextData {
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { ConstructorAbi, DataItem } from '@0xproject/types';
|
||||
import { AbiType, ConstructorAbi, DataItem } from '@0xproject/types';
|
||||
import * as fs from 'fs';
|
||||
import * as _ from 'lodash';
|
||||
import * as path from 'path';
|
||||
|
||||
import { AbiType, ContractsBackend, ParamKind } from './types';
|
||||
import { ContractsBackend, ParamKind } from './types';
|
||||
|
||||
export const utils = {
|
||||
solTypeToTsType(paramKind: ParamKind, backend: ContractsBackend, solType: string, components?: DataItem[]): string {
|
||||
|
@ -1,13 +1,26 @@
|
||||
import { ContractAbi, DataItem, Provider, TxData, TxDataPayable } from '@0xproject/types';
|
||||
import { BigNumber } from '@0xproject/utils';
|
||||
import {
|
||||
AbiDefinition,
|
||||
AbiType,
|
||||
ContractAbi,
|
||||
DataItem,
|
||||
MethodAbi,
|
||||
Provider,
|
||||
TxData,
|
||||
TxDataPayable,
|
||||
} from '@0xproject/types';
|
||||
import { abiUtils, BigNumber } from '@0xproject/utils';
|
||||
import { Web3Wrapper } from '@0xproject/web3-wrapper';
|
||||
import * as ethersContracts from 'ethers-contracts';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { formatABIDataItem } from './utils';
|
||||
|
||||
export interface EthersInterfaceByFunctionSignature {
|
||||
[key: string]: ethersContracts.Interface;
|
||||
}
|
||||
|
||||
export class BaseContract {
|
||||
protected _ethersInterface: ethersContracts.Interface;
|
||||
protected _ethersInterfacesByFunctionSignature: EthersInterfaceByFunctionSignature;
|
||||
protected _web3Wrapper: Web3Wrapper;
|
||||
public abi: ContractAbi;
|
||||
public address: string;
|
||||
@ -49,10 +62,40 @@ export class BaseContract {
|
||||
}
|
||||
return txDataWithDefaults;
|
||||
}
|
||||
protected _lookupEthersInterface(functionSignature: string): ethersContracts.Interface {
|
||||
const ethersInterface = this._ethersInterfacesByFunctionSignature[functionSignature];
|
||||
if (_.isUndefined(ethersInterface)) {
|
||||
throw new Error(`Failed to lookup method with function signature '${functionSignature}'`);
|
||||
}
|
||||
return ethersInterface;
|
||||
}
|
||||
protected _lookupAbi(functionSignature: string): MethodAbi {
|
||||
const methodAbi = _.find(this.abi, (abiDefinition: AbiDefinition) => {
|
||||
if (abiDefinition.type !== AbiType.Function) {
|
||||
return false;
|
||||
}
|
||||
const abiFunctionSignature = abiUtils.getFunctionSignature(abiDefinition);
|
||||
if (abiFunctionSignature === functionSignature) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}) as MethodAbi;
|
||||
return methodAbi;
|
||||
}
|
||||
constructor(abi: ContractAbi, address: string, provider: Provider, defaults?: Partial<TxData>) {
|
||||
this._web3Wrapper = new Web3Wrapper(provider, defaults);
|
||||
this.abi = abi;
|
||||
this.address = address;
|
||||
this._ethersInterface = new ethersContracts.Interface(abi);
|
||||
const methodAbis = this.abi.filter(
|
||||
(abiDefinition: AbiDefinition) => abiDefinition.type === AbiType.Function,
|
||||
) as MethodAbi[];
|
||||
this._ethersInterfacesByFunctionSignature = _.transform(
|
||||
methodAbis,
|
||||
(result: EthersInterfaceByFunctionSignature, methodAbi) => {
|
||||
const functionSignature = abiUtils.getFunctionSignature(methodAbi);
|
||||
result[functionSignature] = new ethersContracts.Interface([methodAbi]);
|
||||
},
|
||||
{},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -41,6 +41,6 @@ export class {{contractName}}Contract extends BaseContract {
|
||||
{{/each}}
|
||||
constructor(abi: ContractAbi, address: string, provider: Provider, defaults?: Partial<TxData>) {
|
||||
super(abi, address, provider, defaults);
|
||||
classUtils.bindAll(this, ['_ethersInterface', 'address', 'abi', '_web3Wrapper']);
|
||||
classUtils.bindAll(this, ['_ethersInterfacesByFunctionSignature', 'address', 'abi', '_web3Wrapper']);
|
||||
}
|
||||
} // tslint:disable:max-file-line-count
|
||||
|
@ -5,9 +5,9 @@ async callAsync(
|
||||
defaultBlock?: BlockParam,
|
||||
): Promise<{{> return_type outputs=outputs}}> {
|
||||
const self = this as any as {{contractName}}Contract;
|
||||
const inputAbi = (_.find(self.abi, {name: '{{this.name}}'}) as MethodAbi).inputs;
|
||||
const inputAbi = self._lookupAbi('{{this.functionSignature}}').inputs;
|
||||
[{{> params inputs=inputs}}] = BaseContract._formatABIDataItemList(inputAbi, [{{> params inputs=inputs}}], BaseContract._bigNumberToString.bind(self));
|
||||
const encodedData = self._ethersInterface.functions.{{this.name}}(
|
||||
const encodedData = self._lookupEthersInterface('{{this.functionSignature}}').functions.{{this.name}}(
|
||||
{{> params inputs=inputs}}
|
||||
).data;
|
||||
const callDataWithDefaults = await self._applyDefaultsToTxDataAsync(
|
||||
|
@ -1,4 +1,4 @@
|
||||
public {{this.name}} = {
|
||||
public {{this.tsName}} = {
|
||||
async sendTransactionAsync(
|
||||
{{> typed_params inputs=inputs}}
|
||||
{{#this.payable}}
|
||||
@ -9,17 +9,17 @@ public {{this.name}} = {
|
||||
{{/this.payable}}
|
||||
): Promise<string> {
|
||||
const self = this as any as {{contractName}}Contract;
|
||||
const inputAbi = (_.find(self.abi, {name: '{{this.name}}'}) as MethodAbi).inputs;
|
||||
const inputAbi = self._lookupAbi('{{this.functionSignature}}').inputs;
|
||||
[{{> params inputs=inputs}}] = BaseContract._formatABIDataItemList(inputAbi, [{{> params inputs=inputs}}], BaseContract._bigNumberToString.bind(self));
|
||||
const encodedData = self._ethersInterface.functions.{{this.name}}(
|
||||
const encodedData = self._lookupEthersInterface('{{this.functionSignature}}').functions.{{this.name}}(
|
||||
{{> params inputs=inputs}}
|
||||
).data
|
||||
).data;
|
||||
const txDataWithDefaults = await self._applyDefaultsToTxDataAsync(
|
||||
{
|
||||
...txData,
|
||||
data: encodedData,
|
||||
},
|
||||
self.{{this.name}}.estimateGasAsync.bind(
|
||||
self.{{this.tsName}}.estimateGasAsync.bind(
|
||||
self,
|
||||
{{> params inputs=inputs}}
|
||||
),
|
||||
@ -32,11 +32,11 @@ public {{this.name}} = {
|
||||
txData: Partial<TxData> = {},
|
||||
): Promise<number> {
|
||||
const self = this as any as {{contractName}}Contract;
|
||||
const inputAbi = (_.find(self.abi, {name: '{{this.name}}'}) as MethodAbi).inputs;
|
||||
const inputAbi = self._lookupAbi('{{this.functionSignature}}').inputs;
|
||||
[{{> params inputs=inputs}}] = BaseContract._formatABIDataItemList(inputAbi, [{{> params inputs=inputs}}], BaseContract._bigNumberToString.bind(this));
|
||||
const encodedData = self._ethersInterface.functions.{{this.name}}(
|
||||
const encodedData = self._lookupEthersInterface('{{this.functionSignature}}').functions.{{this.name}}(
|
||||
{{> params inputs=inputs}}
|
||||
).data
|
||||
).data;
|
||||
const txDataWithDefaults = await self._applyDefaultsToTxDataAsync(
|
||||
{
|
||||
...txData,
|
||||
@ -50,11 +50,11 @@ public {{this.name}} = {
|
||||
{{> typed_params inputs=inputs}}
|
||||
): string {
|
||||
const self = this as any as {{contractName}}Contract;
|
||||
const inputAbi = (_.find(self.abi, {name: '{{this.name}}'}) as MethodAbi).inputs;
|
||||
const inputAbi = self._lookupAbi('{{this.functionSignature}}').inputs;
|
||||
[{{> params inputs=inputs}}] = BaseContract._formatABIDataItemList(inputAbi, [{{> params inputs=inputs}}], BaseContract._bigNumberToString.bind(self));
|
||||
const abiEncodedTransactionData = self._ethersInterface.functions.{{this.name}}(
|
||||
const abiEncodedTransactionData = self._lookupEthersInterface('{{this.name}}').functions.{{this.name}}(
|
||||
{{> params inputs=inputs}}
|
||||
).data
|
||||
).data;
|
||||
return abiEncodedTransactionData;
|
||||
},
|
||||
{{> callAsync}}
|
||||
|
@ -16,7 +16,7 @@
|
||||
"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 src/artifacts",
|
||||
"compile": "node ../deployer/lib/src/cli.js compile --contracts ${npm_package_config_contracts} --contract-dirs src/contracts --artifacts-dir src/artifacts",
|
||||
"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'",
|
||||
"migrate": "yarn build && yarn compile && node ./lib/migrations/index.js",
|
||||
|
@ -47,12 +47,14 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/0x-monorepo/packages/deployer/README.md",
|
||||
"devDependencies": {
|
||||
"@0xproject/dev-utils": "^0.2.1",
|
||||
"@0xproject/monorepo-scripts": "^0.1.16",
|
||||
"@0xproject/tslint-config": "^0.4.14",
|
||||
"@types/require-from-string": "^1.2.0",
|
||||
"@types/semver": "^5.5.0",
|
||||
"@types/yargs": "^11.0.0",
|
||||
"chai": "^4.0.1",
|
||||
"chai-as-promised": "^7.1.0",
|
||||
"copyfiles": "^1.2.0",
|
||||
"dirty-chai": "^2.0.1",
|
||||
"mocha": "^4.0.1",
|
||||
|
@ -11,7 +11,7 @@ import * as yargs from 'yargs';
|
||||
import { commands } from './commands';
|
||||
import { constants } from './utils/constants';
|
||||
import { consoleReporter } from './utils/error_reporter';
|
||||
import { CliOptions, CompilerOptions, DeployerOptions } from './utils/types';
|
||||
import { CliOptions, CompilerOptions, ContractDirectory, DeployerOptions } from './utils/types';
|
||||
|
||||
const DEFAULT_OPTIMIZER_ENABLED = false;
|
||||
const DEFAULT_CONTRACTS_DIR = path.resolve('src/contracts');
|
||||
@ -27,7 +27,7 @@ const DEFAULT_CONTRACTS_LIST = '*';
|
||||
*/
|
||||
async function onCompileCommandAsync(argv: CliOptions): Promise<void> {
|
||||
const opts: CompilerOptions = {
|
||||
contractsDir: argv.contractsDir,
|
||||
contractDirs: getContractDirectoriesFromList(argv.contractDirs),
|
||||
networkId: argv.networkId,
|
||||
optimizerEnabled: argv.shouldOptimize,
|
||||
artifactsDir: argv.artifactsDir,
|
||||
@ -45,7 +45,7 @@ async function onDeployCommandAsync(argv: CliOptions): Promise<void> {
|
||||
const web3Wrapper = new Web3Wrapper(web3Provider);
|
||||
const networkId = await web3Wrapper.getNetworkIdAsync();
|
||||
const compilerOpts: CompilerOptions = {
|
||||
contractsDir: argv.contractsDir,
|
||||
contractDirs: getContractDirectoriesFromList(argv.contractsDir),
|
||||
networkId,
|
||||
optimizerEnabled: argv.shouldOptimize,
|
||||
artifactsDir: argv.artifactsDir,
|
||||
@ -67,6 +67,29 @@ async function onDeployCommandAsync(argv: CliOptions): Promise<void> {
|
||||
const deployerArgs = deployerArgsString.split(',');
|
||||
await commands.deployAsync(argv.contract as string, deployerArgs, deployerOpts);
|
||||
}
|
||||
/**
|
||||
* Creates a set of contracts to compile.
|
||||
* @param contractDirectoriesList Comma separated list of contract directories
|
||||
* @return Set of contract directories
|
||||
*/
|
||||
function getContractDirectoriesFromList(contractDirectoriesList: string): Set<ContractDirectory> {
|
||||
const directories = new Set();
|
||||
const possiblyNamespacedDirectories = contractDirectoriesList.split(',');
|
||||
_.forEach(possiblyNamespacedDirectories, namespacedDirectory => {
|
||||
const directoryComponents = namespacedDirectory.split(':');
|
||||
if (directoryComponents.length === 1) {
|
||||
const directory = { namespace: '', path: directoryComponents[0] };
|
||||
directories.add(directory);
|
||||
} else if (directoryComponents.length === 2) {
|
||||
const directory = { namespace: directoryComponents[0], path: directoryComponents[1] };
|
||||
directories.add(directory);
|
||||
} else {
|
||||
throw new Error(`Unable to parse contracts directory: '${namespacedDirectory}'`);
|
||||
}
|
||||
});
|
||||
|
||||
return directories;
|
||||
}
|
||||
/**
|
||||
* Creates a set of contracts to compile.
|
||||
* @param contracts Comma separated list of contracts to compile
|
||||
@ -78,8 +101,7 @@ function getContractsSetFromList(contracts: string): Set<string> {
|
||||
}
|
||||
const contractsArray = contracts.split(',');
|
||||
_.forEach(contractsArray, contractName => {
|
||||
const fileName = `${contractName}${constants.SOLIDITY_FILE_EXTENSION}`;
|
||||
specifiedContracts.add(fileName);
|
||||
specifiedContracts.add(contractName);
|
||||
});
|
||||
return specifiedContracts;
|
||||
}
|
||||
@ -104,10 +126,11 @@ function deployCommandBuilder(yargsInstance: any) {
|
||||
(() => {
|
||||
const identityCommandBuilder = _.identity;
|
||||
return yargs
|
||||
.option('contracts-dir', {
|
||||
.option('contract-dirs', {
|
||||
type: 'string',
|
||||
default: DEFAULT_CONTRACTS_DIR,
|
||||
description: 'path of contracts directory to compile',
|
||||
description:
|
||||
"comma separated list of contract directories.\nTo avoid filename clashes, directories should be prefixed with a namespace as follows: 'namespace:/path/to/dir'.",
|
||||
})
|
||||
.option('network-id', {
|
||||
type: 'number',
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { ContractAbi } from '@0xproject/types';
|
||||
import { AbiType, ContractAbi, MethodAbi } from '@0xproject/types';
|
||||
import { logUtils, promisify } from '@0xproject/utils';
|
||||
import * as ethUtil from 'ethereumjs-util';
|
||||
import * as fs from 'fs';
|
||||
@ -11,6 +11,8 @@ import solc = require('solc');
|
||||
|
||||
import { binPaths } from './solc/bin_paths';
|
||||
import {
|
||||
constructContractId,
|
||||
constructUniqueSourceFileId,
|
||||
createDirIfDoesNotExistAsync,
|
||||
findImportIfExist,
|
||||
getContractArtifactIfExistsAsync,
|
||||
@ -23,11 +25,14 @@ import { fsWrapper } from './utils/fs_wrapper';
|
||||
import {
|
||||
CompilerOptions,
|
||||
ContractArtifact,
|
||||
ContractDirectory,
|
||||
ContractIds,
|
||||
ContractNetworkData,
|
||||
ContractNetworks,
|
||||
ContractSourceData,
|
||||
ContractSourceDataByFileId,
|
||||
ContractSources,
|
||||
ContractSpecificSourceData,
|
||||
FunctionNameToSeenCount,
|
||||
} from './utils/types';
|
||||
import { utils } from './utils/utils';
|
||||
|
||||
@ -39,20 +44,22 @@ const SOLC_BIN_DIR = path.join(__dirname, '..', '..', 'solc_bin');
|
||||
* to artifact files.
|
||||
*/
|
||||
export class Compiler {
|
||||
private _contractsDir: string;
|
||||
private _contractDirs: Set<ContractDirectory>;
|
||||
private _networkId: number;
|
||||
private _optimizerEnabled: boolean;
|
||||
private _artifactsDir: string;
|
||||
// This get's set in the beggining of `compileAsync` function. It's not called from a constructor, but it's the only public method of that class and could as well be.
|
||||
private _contractSources!: ContractSources;
|
||||
private _specifiedContracts: Set<string> = new Set();
|
||||
private _contractSourceData: ContractSourceData = {};
|
||||
private _contractSourceDataByFileId: ContractSourceDataByFileId = {};
|
||||
|
||||
/**
|
||||
* Recursively retrieves Solidity source code from directory.
|
||||
* @param dirPath Directory to search.
|
||||
* @return Mapping of contract fileName to contract source.
|
||||
* @param contractBaseDir Base contracts directory of search tree.
|
||||
* @return Mapping of sourceFilePath to the contract source.
|
||||
*/
|
||||
private static async _getContractSourcesAsync(dirPath: string): Promise<ContractSources> {
|
||||
private static async _getContractSourcesAsync(dirPath: string, contractBaseDir: string): Promise<ContractSources> {
|
||||
let dirContents: string[] = [];
|
||||
try {
|
||||
dirContents = await fsWrapper.readdirAsync(dirPath);
|
||||
@ -68,14 +75,15 @@ export class Compiler {
|
||||
encoding: 'utf8',
|
||||
};
|
||||
const source = await fsWrapper.readFileAsync(contentPath, opts);
|
||||
sources[fileName] = source;
|
||||
logUtils.log(`Reading ${fileName} source...`);
|
||||
const sourceFilePath = contentPath.slice(contractBaseDir.length);
|
||||
sources[sourceFilePath] = source;
|
||||
logUtils.log(`Reading ${sourceFilePath} source...`);
|
||||
} catch (err) {
|
||||
logUtils.log(`Could not find file at ${contentPath}`);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
const nestedSources = await Compiler._getContractSourcesAsync(contentPath);
|
||||
const nestedSources = await Compiler._getContractSourcesAsync(contentPath, contractBaseDir);
|
||||
sources = {
|
||||
...sources,
|
||||
...nestedSources,
|
||||
@ -93,7 +101,7 @@ export class Compiler {
|
||||
* @return An instance of the Compiler class.
|
||||
*/
|
||||
constructor(opts: CompilerOptions) {
|
||||
this._contractsDir = opts.contractsDir;
|
||||
this._contractDirs = opts.contractDirs;
|
||||
this._networkId = opts.networkId;
|
||||
this._optimizerEnabled = opts.optimizerEnabled;
|
||||
this._artifactsDir = opts.artifactsDir;
|
||||
@ -105,25 +113,47 @@ export class Compiler {
|
||||
public async compileAsync(): Promise<void> {
|
||||
await createDirIfDoesNotExistAsync(this._artifactsDir);
|
||||
await createDirIfDoesNotExistAsync(SOLC_BIN_DIR);
|
||||
this._contractSources = await Compiler._getContractSourcesAsync(this._contractsDir);
|
||||
_.forIn(this._contractSources, this._setContractSpecificSourceData.bind(this));
|
||||
const fileNames = this._specifiedContracts.has(ALL_CONTRACTS_IDENTIFIER)
|
||||
? _.keys(this._contractSources)
|
||||
: Array.from(this._specifiedContracts.values());
|
||||
for (const fileName of fileNames) {
|
||||
await this._compileContractAsync(fileName);
|
||||
this._contractSources = {};
|
||||
const contractIds: ContractIds = {};
|
||||
const contractDirs = Array.from(this._contractDirs.values());
|
||||
for (const contractDir of contractDirs) {
|
||||
const sources = await Compiler._getContractSourcesAsync(contractDir.path, contractDir.path);
|
||||
_.forIn(sources, (source, sourceFilePath) => {
|
||||
const sourceFileId = constructUniqueSourceFileId(contractDir.namespace, sourceFilePath);
|
||||
// Record the file's source and data
|
||||
if (!_.isUndefined(this._contractSources[sourceFileId])) {
|
||||
throw new Error(`Found duplicate source files with ID '${sourceFileId}'`);
|
||||
}
|
||||
this._contractSources[sourceFileId] = source;
|
||||
// Create a mapping between the contract id and its source file id
|
||||
const contractId = constructContractId(contractDir.namespace, sourceFilePath);
|
||||
if (!_.isUndefined(contractIds[contractId])) {
|
||||
throw new Error(`Found duplicate contract with ID '${contractId}'`);
|
||||
}
|
||||
contractIds[contractId] = sourceFileId;
|
||||
});
|
||||
}
|
||||
_.forIn(this._contractSources, this._setContractSpecificSourceData.bind(this));
|
||||
const specifiedContractIds = this._specifiedContracts.has(ALL_CONTRACTS_IDENTIFIER)
|
||||
? _.keys(contractIds)
|
||||
: Array.from(this._specifiedContracts.values());
|
||||
await Promise.all(
|
||||
_.map(specifiedContractIds, async contractId => this._compileContractAsync(contractIds[contractId])),
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Compiles contract and saves artifact to artifactsDir.
|
||||
* @param fileName Name of contract with '.sol' extension.
|
||||
* @param sourceFileId Unique ID of the source file.
|
||||
*/
|
||||
private async _compileContractAsync(fileName: string): Promise<void> {
|
||||
private async _compileContractAsync(sourceFileId: string): Promise<void> {
|
||||
if (_.isUndefined(this._contractSources)) {
|
||||
throw new Error('Contract sources not yet initialized');
|
||||
}
|
||||
const contractSpecificSourceData = this._contractSourceData[fileName];
|
||||
const currentArtifactIfExists = await getContractArtifactIfExistsAsync(this._artifactsDir, fileName);
|
||||
if (_.isUndefined(this._contractSourceDataByFileId[sourceFileId])) {
|
||||
throw new Error(`Contract source for ${sourceFileId} not yet initialized`);
|
||||
}
|
||||
const contractSpecificSourceData = this._contractSourceDataByFileId[sourceFileId];
|
||||
const currentArtifactIfExists = await getContractArtifactIfExistsAsync(this._artifactsDir, sourceFileId);
|
||||
const sourceHash = `0x${contractSpecificSourceData.sourceHash.toString('hex')}`;
|
||||
const sourceTreeHash = `0x${contractSpecificSourceData.sourceTreeHash.toString('hex')}`;
|
||||
|
||||
@ -162,16 +192,17 @@ export class Compiler {
|
||||
}
|
||||
const solcInstance = solc.setupMethods(requireFromString(solcjs, compilerBinFilename));
|
||||
|
||||
logUtils.log(`Compiling ${fileName} with Solidity v${solcVersion}...`);
|
||||
const source = this._contractSources[fileName];
|
||||
logUtils.log(`Compiling ${sourceFileId} with Solidity v${solcVersion}...`);
|
||||
const source = this._contractSources[sourceFileId];
|
||||
const input = {
|
||||
[fileName]: source,
|
||||
[sourceFileId]: source,
|
||||
};
|
||||
const sourcesToCompile = {
|
||||
sources: input,
|
||||
};
|
||||
|
||||
const compiled = solcInstance.compile(sourcesToCompile, Number(this._optimizerEnabled), importPath =>
|
||||
findImportIfExist(this._contractSources, importPath),
|
||||
findImportIfExist(this._contractSources, sourceFileId, importPath),
|
||||
);
|
||||
|
||||
if (!_.isUndefined(compiled.errors)) {
|
||||
@ -193,11 +224,11 @@ export class Compiler {
|
||||
});
|
||||
}
|
||||
}
|
||||
const contractName = path.basename(fileName, constants.SOLIDITY_FILE_EXTENSION);
|
||||
const contractIdentifier = `${fileName}:${contractName}`;
|
||||
const contractName = path.basename(sourceFileId, constants.SOLIDITY_FILE_EXTENSION);
|
||||
const contractIdentifier = `${sourceFileId}:${contractName}`;
|
||||
if (_.isUndefined(compiled.contracts[contractIdentifier])) {
|
||||
throw new Error(
|
||||
`Contract ${contractName} not found in ${fileName}. Please make sure your contract has the same name as it's file name`,
|
||||
`Contract ${contractName} not found in ${sourceFileId}. Please make sure your contract has the same name as it's file name`,
|
||||
);
|
||||
}
|
||||
const abi: ContractAbi = JSON.parse(compiled.contracts[contractIdentifier].interface);
|
||||
@ -207,6 +238,7 @@ export class Compiler {
|
||||
const sourceMapRuntime = compiled.contracts[contractIdentifier].srcmapRuntime;
|
||||
const sources = _.keys(compiled.sources);
|
||||
const updated_at = Date.now();
|
||||
|
||||
const contractNetworkData: ContractNetworkData = {
|
||||
solc_version: solcVersion,
|
||||
keccak256: sourceHash,
|
||||
@ -243,28 +275,30 @@ export class Compiler {
|
||||
const artifactString = utils.stringifyWithFormatting(newArtifact);
|
||||
const currentArtifactPath = `${this._artifactsDir}/${contractName}.json`;
|
||||
await fsWrapper.writeFileAsync(currentArtifactPath, artifactString);
|
||||
logUtils.log(`${fileName} artifact saved!`);
|
||||
logUtils.log(`${sourceFileId} artifact saved!`);
|
||||
}
|
||||
/**
|
||||
* Gets contract dependendencies and keccak256 hash from source.
|
||||
* @param source Source code of contract.
|
||||
* @param fileId FileId of the contract source file.
|
||||
* @return Object with contract dependencies and keccak256 hash of source.
|
||||
*/
|
||||
private _setContractSpecificSourceData(source: string, fileName: string): void {
|
||||
if (!_.isUndefined(this._contractSourceData[fileName])) {
|
||||
private _setContractSpecificSourceData(source: string, fileId: string): void {
|
||||
if (!_.isUndefined(this._contractSourceDataByFileId[fileId])) {
|
||||
return;
|
||||
}
|
||||
const sourceHash = ethUtil.sha3(source);
|
||||
const solcVersionRange = parseSolidityVersionRange(source);
|
||||
const dependencies = parseDependencies(source);
|
||||
const sourceTreeHash = this._getSourceTreeHash(fileName, sourceHash, dependencies);
|
||||
this._contractSourceData[fileName] = {
|
||||
const dependencies = parseDependencies(source, fileId);
|
||||
const sourceTreeHash = this._getSourceTreeHash(fileId, sourceHash, dependencies);
|
||||
this._contractSourceDataByFileId[fileId] = {
|
||||
dependencies,
|
||||
solcVersionRange,
|
||||
sourceHash,
|
||||
sourceTreeHash,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the source tree hash for a file and its dependencies.
|
||||
* @param fileName Name of contract file.
|
||||
@ -276,7 +310,7 @@ export class Compiler {
|
||||
const dependencySourceTreeHashes = _.map(dependencies, dependency => {
|
||||
const source = this._contractSources[dependency];
|
||||
this._setContractSpecificSourceData(source, dependency);
|
||||
const sourceData = this._contractSourceData[dependency];
|
||||
const sourceData = this._contractSourceDataByFileId[dependency];
|
||||
return this._getSourceTreeHash(dependency, sourceData.sourceHash, sourceData.dependencies);
|
||||
});
|
||||
const sourceTreeHashesBuffer = Buffer.concat([sourceHash, ...dependencySourceTreeHashes]);
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { AbiType, ContractAbi, MethodAbi } from '@0xproject/types';
|
||||
import { logUtils } from '@0xproject/utils';
|
||||
import * as _ from 'lodash';
|
||||
import * as path from 'path';
|
||||
@ -5,8 +6,48 @@ import * as solc from 'solc';
|
||||
|
||||
import { constants } from './constants';
|
||||
import { fsWrapper } from './fs_wrapper';
|
||||
import { ContractArtifact, ContractSources } from './types';
|
||||
import { ContractArtifact, ContractSources, FunctionNameToSeenCount } from './types';
|
||||
|
||||
/**
|
||||
* Constructs a system-wide unique identifier for a source file.
|
||||
* @param directoryNamespace Namespace of the source file's root contract directory.
|
||||
* @param sourceFilePath Path to a source file, relative to contractBaseDir.
|
||||
* @return sourceFileId A system-wide unique identifier for the source file.
|
||||
*/
|
||||
export function constructUniqueSourceFileId(directoryNamespace: string, sourceFilePath: string): string {
|
||||
const namespacePrefix = !_.isEmpty(directoryNamespace) ? `/${directoryNamespace}` : '';
|
||||
const sourceFilePathNoLeadingSlash = sourceFilePath.replace(/^\/+/g, '');
|
||||
const sourceFileId = `${namespacePrefix}/${sourceFilePathNoLeadingSlash}`;
|
||||
return sourceFileId;
|
||||
}
|
||||
/**
|
||||
* Constructs a system-wide unique identifier for a dependency file.
|
||||
* @param dependencyFilePath Path from a sourceFile to a dependency.
|
||||
* @param contractBaseDir Base contracts directory of search tree.
|
||||
* @return sourceFileId A system-wide unique identifier for the source file.
|
||||
*/
|
||||
export function constructDependencyFileId(dependencyFilePath: string, sourceFilePath: string): string {
|
||||
if (_.startsWith(dependencyFilePath, '/')) {
|
||||
// Path of the form /namespace/path/to/dependency.sol
|
||||
return dependencyFilePath;
|
||||
} else {
|
||||
// Dependency is relative to the source file: ./dependency.sol, ../../some/path/dependency.sol, etc.
|
||||
// Join the two paths to construct a valid source file id: /namespace/path/to/dependency.sol
|
||||
return path.join(path.dirname(sourceFilePath), dependencyFilePath);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Constructs a system-wide unique identifier for a contract.
|
||||
* @param directoryNamespace Namespace of the source file's root contract directory.
|
||||
* @param sourceFilePath Path to a source file, relative to contractBaseDir.
|
||||
* @return sourceFileId A system-wide unique identifier for contract.
|
||||
*/
|
||||
export function constructContractId(directoryNamespace: string, sourceFilePath: string): string {
|
||||
const namespacePrefix = !_.isEmpty(directoryNamespace) ? `${directoryNamespace}:` : '';
|
||||
const sourceFileName = path.basename(sourceFilePath, constants.SOLIDITY_FILE_EXTENSION);
|
||||
const contractId = `${namespacePrefix}${sourceFileName}`;
|
||||
return contractId;
|
||||
}
|
||||
/**
|
||||
* Gets contract data on network or returns if an artifact does not exist.
|
||||
* @param artifactsDir Path to the artifacts directory.
|
||||
@ -82,9 +123,10 @@ export function getNormalizedErrMsg(errMsg: string): string {
|
||||
/**
|
||||
* Parses the contract source code and extracts the dendencies
|
||||
* @param source Contract source code
|
||||
* @param sourceFilePath File path of the source code.
|
||||
* @return List of dependendencies
|
||||
*/
|
||||
export function parseDependencies(source: string): string[] {
|
||||
export function parseDependencies(source: string, sourceFileId: string): string[] {
|
||||
// TODO: Use a proper parser
|
||||
const IMPORT_REGEX = /(import\s)/;
|
||||
const DEPENDENCY_PATH_REGEX = /"([^"]+)"/; // Source: https://github.com/BlockChainCompany/soljitsu/blob/master/lib/shared.js
|
||||
@ -95,8 +137,8 @@ export function parseDependencies(source: string): string[] {
|
||||
const dependencyMatch = line.match(DEPENDENCY_PATH_REGEX);
|
||||
if (!_.isNull(dependencyMatch)) {
|
||||
const dependencyPath = dependencyMatch[1];
|
||||
const basenName = path.basename(dependencyPath);
|
||||
dependencies.push(basenName);
|
||||
const dependencyId = constructDependencyFileId(dependencyPath, sourceFileId);
|
||||
dependencies.push(dependencyId);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -107,14 +149,19 @@ export function parseDependencies(source: string): string[] {
|
||||
* Callback to resolve dependencies with `solc.compile`.
|
||||
* Throws error if contractSources not yet initialized.
|
||||
* @param contractSources Source codes of contracts.
|
||||
* @param importPath Path to an imported dependency.
|
||||
* @param sourceFileId ID of the source file.
|
||||
* @param importPath Path of dependency source file.
|
||||
* @return Import contents object containing source code of dependency.
|
||||
*/
|
||||
export function findImportIfExist(contractSources: ContractSources, importPath: string): solc.ImportContents {
|
||||
const fileName = path.basename(importPath);
|
||||
const source = contractSources[fileName];
|
||||
export function findImportIfExist(
|
||||
contractSources: ContractSources,
|
||||
sourceFileId: string,
|
||||
importPath: string,
|
||||
): solc.ImportContents {
|
||||
const dependencyFileId = constructDependencyFileId(importPath, sourceFileId);
|
||||
const source = contractSources[dependencyFileId];
|
||||
if (_.isUndefined(source)) {
|
||||
throw new Error(`Contract source not found for ${fileName}`);
|
||||
throw new Error(`Contract source not found for ${dependencyFileId}`);
|
||||
}
|
||||
const importContents: solc.ImportContents = {
|
||||
contents: source,
|
||||
|
@ -1,11 +1,9 @@
|
||||
import { schemas, SchemaValidator } from '@0xproject/json-schemas';
|
||||
import { ContractAbi, EventAbi, FunctionAbi, MethodAbi, TxData } from '@0xproject/types';
|
||||
import { AbiType, ContractAbi, EventAbi, FunctionAbi, MethodAbi, TxData } from '@0xproject/types';
|
||||
import { promisify } from '@0xproject/utils';
|
||||
import * as _ from 'lodash';
|
||||
import * as Web3 from 'web3';
|
||||
|
||||
import { AbiType } from './types';
|
||||
|
||||
export class Contract implements Web3.ContractInstance {
|
||||
public address: string;
|
||||
public abi: ContractAbi;
|
||||
|
@ -1,9 +1,7 @@
|
||||
import { AbiDefinition, ContractAbi, DataItem } from '@0xproject/types';
|
||||
import { AbiDefinition, AbiType, ContractAbi, DataItem } from '@0xproject/types';
|
||||
import * as _ from 'lodash';
|
||||
import * as web3Abi from 'web3-eth-abi';
|
||||
|
||||
import { AbiType } from './types';
|
||||
|
||||
export const encoder = {
|
||||
encodeConstructorArgsFromAbi(args: any[], abi: ContractAbi): string {
|
||||
const constructorTypes: string[] = [];
|
||||
|
@ -18,6 +18,11 @@ export interface ContractNetworks {
|
||||
[key: number]: ContractNetworkData;
|
||||
}
|
||||
|
||||
export interface ContractDirectory {
|
||||
path: string;
|
||||
namespace: string;
|
||||
}
|
||||
|
||||
export interface ContractNetworkData {
|
||||
solc_version: string;
|
||||
optimizer_enabled: boolean;
|
||||
@ -40,7 +45,7 @@ export interface SolcErrors {
|
||||
|
||||
export interface CliOptions extends yargs.Arguments {
|
||||
artifactsDir: string;
|
||||
contractsDir: string;
|
||||
contractDirs: string;
|
||||
jsonrpcUrl: string;
|
||||
networkId: number;
|
||||
shouldOptimize: boolean;
|
||||
@ -51,7 +56,7 @@ export interface CliOptions extends yargs.Arguments {
|
||||
}
|
||||
|
||||
export interface CompilerOptions {
|
||||
contractsDir: string;
|
||||
contractDirs: Set<ContractDirectory>;
|
||||
networkId: number;
|
||||
optimizerEnabled: boolean;
|
||||
artifactsDir: string;
|
||||
@ -78,7 +83,11 @@ export interface ContractSources {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
export interface ContractSourceData {
|
||||
export interface ContractIds {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
export interface ContractSourceDataByFileId {
|
||||
[key: string]: ContractSpecificSourceData;
|
||||
}
|
||||
|
||||
@ -98,4 +107,8 @@ export interface Token {
|
||||
swarmHash: string;
|
||||
}
|
||||
|
||||
export interface FunctionNameToSeenCount {
|
||||
[key: string]: number;
|
||||
}
|
||||
|
||||
export type DoneCallback = (err?: Error) => void;
|
||||
|
@ -3,7 +3,13 @@ import 'mocha';
|
||||
|
||||
import { Compiler } from '../src/compiler';
|
||||
import { fsWrapper } from '../src/utils/fs_wrapper';
|
||||
import { CompilerOptions, ContractArtifact, ContractNetworkData, DoneCallback } from '../src/utils/types';
|
||||
import {
|
||||
CompilerOptions,
|
||||
ContractArtifact,
|
||||
ContractDirectory,
|
||||
ContractNetworkData,
|
||||
DoneCallback,
|
||||
} from '../src/utils/types';
|
||||
|
||||
import { exchange_binary } from './fixtures/exchange_bin';
|
||||
import { constants } from './util/constants';
|
||||
@ -13,11 +19,15 @@ const expect = chai.expect;
|
||||
describe('#Compiler', function() {
|
||||
this.timeout(constants.timeoutMs);
|
||||
const artifactsDir = `${__dirname}/fixtures/artifacts`;
|
||||
const contractsDir = `${__dirname}/fixtures/contracts`;
|
||||
const mainContractDir: ContractDirectory = { path: `${__dirname}/fixtures/contracts/main`, namespace: 'main' };
|
||||
const baseContractDir: ContractDirectory = { path: `${__dirname}/fixtures/contracts/base`, namespace: 'base' };
|
||||
const contractDirs: Set<ContractDirectory> = new Set();
|
||||
contractDirs.add(mainContractDir);
|
||||
contractDirs.add(baseContractDir);
|
||||
const exchangeArtifactPath = `${artifactsDir}/Exchange.json`;
|
||||
const compilerOpts: CompilerOptions = {
|
||||
artifactsDir,
|
||||
contractsDir,
|
||||
contractDirs,
|
||||
networkId: constants.networkId,
|
||||
optimizerEnabled: constants.optimizerEnabled,
|
||||
specifiedContracts: new Set(constants.specifiedContracts),
|
||||
|
@ -47,28 +47,34 @@ describe('Compiler utils', () => {
|
||||
});
|
||||
describe('#parseDependencies', () => {
|
||||
it('correctly parses Exchange dependencies', async () => {
|
||||
const exchangeSource = await fsWrapper.readFileAsync(`${__dirname}/fixtures/contracts/Exchange.sol`, {
|
||||
const exchangeSource = await fsWrapper.readFileAsync(`${__dirname}/fixtures/contracts/main/Exchange.sol`, {
|
||||
encoding: 'utf8',
|
||||
});
|
||||
expect(parseDependencies(exchangeSource)).to.be.deep.equal([
|
||||
'TokenTransferProxy.sol',
|
||||
'Token.sol',
|
||||
'SafeMath.sol',
|
||||
const sourceFileId = '/main/Exchange.sol';
|
||||
expect(parseDependencies(exchangeSource, sourceFileId)).to.be.deep.equal([
|
||||
'/main/TokenTransferProxy.sol',
|
||||
'/base/Token.sol',
|
||||
'/base/SafeMath.sol',
|
||||
]);
|
||||
});
|
||||
it('correctly parses TokenTransferProxy dependencies', async () => {
|
||||
const exchangeSource = await fsWrapper.readFileAsync(
|
||||
`${__dirname}/fixtures/contracts/TokenTransferProxy.sol`,
|
||||
`${__dirname}/fixtures/contracts/main/TokenTransferProxy.sol`,
|
||||
{
|
||||
encoding: 'utf8',
|
||||
},
|
||||
);
|
||||
expect(parseDependencies(exchangeSource)).to.be.deep.equal(['Token.sol', 'Ownable.sol']);
|
||||
const sourceFileId = '/main/TokenTransferProxy.sol';
|
||||
expect(parseDependencies(exchangeSource, sourceFileId)).to.be.deep.equal([
|
||||
'/base/Token.sol',
|
||||
'/base/Ownable.sol',
|
||||
]);
|
||||
});
|
||||
// TODO: For now that doesn't work. This will work after we switch to a grammar-based parser
|
||||
it.skip('correctly parses commented out dependencies', async () => {
|
||||
const contractWithCommentedOutDependencies = `// import "./TokenTransferProxy.sol";`;
|
||||
expect(parseDependencies(contractWithCommentedOutDependencies)).to.be.deep.equal([]);
|
||||
const sourceFileId = '/main/TokenTransferProxy.sol';
|
||||
expect(parseDependencies(contractWithCommentedOutDependencies, sourceFileId)).to.be.deep.equal([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -4,7 +4,13 @@ import 'mocha';
|
||||
import { Compiler } from '../src/compiler';
|
||||
import { Deployer } from '../src/deployer';
|
||||
import { fsWrapper } from '../src/utils/fs_wrapper';
|
||||
import { CompilerOptions, ContractArtifact, ContractNetworkData, DoneCallback } from '../src/utils/types';
|
||||
import {
|
||||
CompilerOptions,
|
||||
ContractArtifact,
|
||||
ContractDirectory,
|
||||
ContractNetworkData,
|
||||
DoneCallback,
|
||||
} from '../src/utils/types';
|
||||
|
||||
import { constructor_args, exchange_binary } from './fixtures/exchange_bin';
|
||||
import { constants } from './util/constants';
|
||||
@ -13,11 +19,15 @@ const expect = chai.expect;
|
||||
|
||||
describe('#Deployer', () => {
|
||||
const artifactsDir = `${__dirname}/fixtures/artifacts`;
|
||||
const contractsDir = `${__dirname}/fixtures/contracts`;
|
||||
const exchangeArtifactPath = `${artifactsDir}/Exchange.json`;
|
||||
const mainContractDir: ContractDirectory = { path: `${__dirname}/fixtures/contracts/main`, namespace: '' };
|
||||
const baseContractDir: ContractDirectory = { path: `${__dirname}/fixtures/contracts/base`, namespace: 'base' };
|
||||
const contractDirs: Set<ContractDirectory> = new Set();
|
||||
contractDirs.add(mainContractDir);
|
||||
contractDirs.add(baseContractDir);
|
||||
const compilerOpts: CompilerOptions = {
|
||||
artifactsDir,
|
||||
contractsDir,
|
||||
contractDirs,
|
||||
networkId: constants.networkId,
|
||||
optimizerEnabled: constants.optimizerEnabled,
|
||||
specifiedContracts: new Set(constants.specifiedContracts),
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright 2017 ZeroEx Intl.
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@ -19,8 +19,8 @@
|
||||
pragma solidity 0.4.14;
|
||||
|
||||
import "./TokenTransferProxy.sol";
|
||||
import "./base/Token.sol";
|
||||
import "./base/SafeMath.sol";
|
||||
import "/base/Token.sol";
|
||||
import "/base/SafeMath.sol";
|
||||
|
||||
/// @title Exchange - Facilitates exchange of ERC20 tokens.
|
||||
/// @author Amir Bandeali - <amir@0xProject.com>, Will Warren - <will@0xProject.com>
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright 2017 ZeroEx Intl.
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@ -18,8 +18,8 @@
|
||||
|
||||
pragma solidity 0.4.14;
|
||||
|
||||
import "./base/Token.sol";
|
||||
import "./base/Ownable.sol";
|
||||
import "/base/Token.sol";
|
||||
import "/base/Ownable.sol";
|
||||
|
||||
/// @title TokenTransferProxy - Transfers tokens on behalf of contracts that have been approved via decentralized governance.
|
||||
/// @author Amir Bandeali - <amir@0xProject.com>, Will Warren - <will@0xProject.com>
|
@ -3,10 +3,49 @@
|
||||
"networks": {
|
||||
"50": {
|
||||
"solc_version": "0.4.21",
|
||||
"keccak256": "0x2c3aa2e9dbef58abf57cecc148464d0852a83d7f30bbd2066f2a13b8bd3b1dd0",
|
||||
"source_tree_hash": "0x2c3aa2e9dbef58abf57cecc148464d0852a83d7f30bbd2066f2a13b8bd3b1dd0",
|
||||
"keccak256": "0x85fb29ea6c21adcf07f754b2ad06482dd6fcd62d31935e36041b4d064c6a038e",
|
||||
"source_tree_hash": "0x85fb29ea6c21adcf07f754b2ad06482dd6fcd62d31935e36041b4d064c6a038e",
|
||||
"optimizer_enabled": false,
|
||||
"abi": [
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"components": [
|
||||
{
|
||||
"components": [
|
||||
{
|
||||
"name": "to",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "amount",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "transferData",
|
||||
"type": "tuple"
|
||||
},
|
||||
{
|
||||
"name": "callback",
|
||||
"type": "uint32"
|
||||
}
|
||||
],
|
||||
"name": "nestedTransferData",
|
||||
"type": "tuple"
|
||||
}
|
||||
],
|
||||
"name": "transfer",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "int256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
@ -55,6 +94,39 @@
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"components": [
|
||||
{
|
||||
"name": "to",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "amount",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "transferData",
|
||||
"type": "tuple"
|
||||
},
|
||||
{
|
||||
"name": "callback",
|
||||
"type": "uint32"
|
||||
}
|
||||
],
|
||||
"name": "transfer",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "int256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"payable": false,
|
||||
@ -84,15 +156,14 @@
|
||||
"type": "event"
|
||||
}
|
||||
],
|
||||
"bytecode":
|
||||
"0x6060604052341561000f57600080fd5b6127106000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550610406806100636000396000f30060606040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806327e235e3146100515780632bd14bb914610087575b600080fd5b341561005c57600080fd5b610071600461006c9036906102b9565b6100bd565b60405161007e9190610344565b60405180910390f35b341561009257600080fd5b6100a760046100a29036906102e2565b6100d5565b6040516100b49190610329565b60405180910390f35b60006020528060005260406000206000915090505481565b600081602001516000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101561012a5760009050610240565b81602001516000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055508160200151600080846000015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540192505081905550816000015173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84602001516040516102339190610344565b60405180910390a3600190505b919050565b600061025182356103a2565b905092915050565b60006040828403121561026b57600080fd5b610275604061035f565b9050600061028584828501610245565b6000830152506020610299848285016102a5565b60208301525092915050565b60006102b182356103c2565b905092915050565b6000602082840312156102cb57600080fd5b60006102d984828501610245565b91505092915050565b6000604082840312156102f457600080fd5b600061030284828501610259565b91505092915050565b6103148161038c565b82525050565b61032381610398565b82525050565b600060208201905061033e600083018461030b565b92915050565b6000602082019050610359600083018461031a565b92915050565b6000604051905081810181811067ffffffffffffffff8211171561038257600080fd5b8060405250919050565b60008115159050919050565b6000819050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60008190509190505600a265627a7a72305820d15828219194e8ddaa624e10f9c8823c05268d79753b4c60ef401fb4fe5f09dc6c6578706572696d656e74616cf50037",
|
||||
"runtime_bytecode":
|
||||
"0x60606040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806327e235e3146100515780632bd14bb914610087575b600080fd5b341561005c57600080fd5b610071600461006c9036906102b9565b6100bd565b60405161007e9190610344565b60405180910390f35b341561009257600080fd5b6100a760046100a29036906102e2565b6100d5565b6040516100b49190610329565b60405180910390f35b60006020528060005260406000206000915090505481565b600081602001516000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101561012a5760009050610240565b81602001516000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055508160200151600080846000015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540192505081905550816000015173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84602001516040516102339190610344565b60405180910390a3600190505b919050565b600061025182356103a2565b905092915050565b60006040828403121561026b57600080fd5b610275604061035f565b9050600061028584828501610245565b6000830152506020610299848285016102a5565b60208301525092915050565b60006102b182356103c2565b905092915050565b6000602082840312156102cb57600080fd5b60006102d984828501610245565b91505092915050565b6000604082840312156102f457600080fd5b600061030284828501610259565b91505092915050565b6103148161038c565b82525050565b61032381610398565b82525050565b600060208201905061033e600083018461030b565b92915050565b6000602082019050610359600083018461031a565b92915050565b6000604051905081810181811067ffffffffffffffff8211171561038257600080fd5b8060405250919050565b60008115159050919050565b6000819050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60008190509190505600a265627a7a72305820d15828219194e8ddaa624e10f9c8823c05268d79753b4c60ef401fb4fe5f09dc6c6578706572696d656e74616cf50037",
|
||||
"updated_at": 1522318279735,
|
||||
"source_map": "60:662:0:-;;;290:72;;;;;;;;350:5;327:8;:20;336:10;327:20;;;;;;;;;;;;;;;:28;;;;60:662;;;;;;",
|
||||
"source_map_runtime":
|
||||
"60:662:0:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;84:41;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;368:352;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;84:41;;;;;;;;;;;;;;;;;:::o;368:352::-;429:12;480;:19;;;457:8;:20;466:10;457:20;;;;;;;;;;;;;;;;:42;453:60;;;508:5;501:12;;;;453:60;547:12;:19;;;523:8;:20;532:10;523:20;;;;;;;;;;;;;;;;:43;;;;;;;;;;;605:12;:19;;;576:8;:25;585:12;:15;;;576:25;;;;;;;;;;;;;;;;:48;;;;;;;;;;;655:12;:15;;;634:58;;643:10;634:58;;;672:12;:19;;;634:58;;;;;;;;;;;;;;;709:4;702:11;;368:352;;;;:::o;5:118:-1:-;;72:46;110:6;97:20;72:46;;;63:55;;57:66;;;;;165:469;;282:4;270:9;265:3;261:19;257:30;254:2;;;300:1;297;290:12;254:2;318:20;333:4;318:20;;;309:29;;386:1;417:49;462:3;453:6;442:9;438:22;417:49;;;411:3;404:5;400:15;393:74;348:130;530:2;563:49;608:3;599:6;588:9;584:22;563:49;;;556:4;549:5;545:16;538:75;488:136;248:386;;;;;641:118;;708:46;746:6;733:20;708:46;;;699:55;;693:66;;;;;766:241;;870:2;858:9;849:7;845:23;841:32;838:2;;;886:1;883;876:12;838:2;921:1;938:53;983:7;974:6;963:9;959:22;938:53;;;928:63;;900:97;832:175;;;;;1014:297;;1146:2;1134:9;1125:7;1121:23;1117:32;1114:2;;;1162:1;1159;1152:12;1114:2;1197:1;1214:81;1287:7;1278:6;1267:9;1263:22;1214:81;;;1204:91;;1176:125;1108:203;;;;;1318:101;1385:28;1407:5;1385:28;;;1380:3;1373:41;1367:52;;;1426:110;1499:31;1524:5;1499:31;;;1494:3;1487:44;1481:55;;;1543:181;;1645:2;1634:9;1630:18;1622:26;;1659:55;1711:1;1700:9;1696:17;1687:6;1659:55;;;1616:108;;;;;1731:193;;1839:2;1828:9;1824:18;1816:26;;1853:61;1911:1;1900:9;1896:17;1887:6;1853:61;;;1810:114;;;;;1931:256;;1993:2;1987:9;1977:19;;2031:4;2023:6;2019:17;2130:6;2118:10;2115:22;2094:18;2082:10;2079:34;2076:62;2073:2;;;2151:1;2148;2141:12;2073:2;2171:10;2167:2;2160:22;1971:216;;;;;2194:92;;2274:5;2267:13;2260:21;2249:32;;2243:43;;;;2293:79;;2362:5;2351:16;;2345:27;;;;2379:128;;2459:42;2452:5;2448:54;2437:65;;2431:76;;;;2514:79;;2583:5;2572:16;;2566:27;;;",
|
||||
"sources": ["Metacoin.sol"]
|
||||
"bytecode": "0x6060604052341561000f57600080fd5b6127106000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550610613806100636000396000f300606060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063135cfdb11461006757806327e235e31461009d5780632bd14bb9146100d35780632f8086ba14610109575b600080fd5b341561007257600080fd5b6100876004610082903690610446565b61013f565b604051610094919061051c565b60405180910390f35b34156100a857600080fd5b6100bd60046100b890369061041d565b61015a565b6040516100ca9190610537565b60405180910390f35b34156100de57600080fd5b6100f360046100ee90369061046f565b610172565b6040516101009190610501565b60405180910390f35b341561011457600080fd5b6101296004610124903690610498565b6102e2565b604051610136919061051c565b60405180910390f35b6000610153826000015183602001516102e2565b9050919050565b60006020528060005260406000206000915090505481565b600081602001516000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410156101c757600090506102dd565b81602001516000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055508160200151600080846000015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540192505081905550816000015173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84602001516040516102d09190610537565b60405180910390a3600190505b919050565b60006102ed83610172565b508163ffffffff16905092915050565b6000610309823561059f565b905092915050565b60006060828403121561032357600080fd5b61032d6040610552565b9050600061033d8482850161035d565b600083015250604061035184828501610409565b60208301525092915050565b60006040828403121561036f57600080fd5b6103796040610552565b90506000610389848285016102fd565b600083015250602061039d848285016103f5565b60208301525092915050565b6000604082840312156103bb57600080fd5b6103c56040610552565b905060006103d5848285016102fd565b60008301525060206103e9848285016103f5565b60208301525092915050565b600061040182356105bf565b905092915050565b600061041582356105c9565b905092915050565b60006020828403121561042f57600080fd5b600061043d848285016102fd565b91505092915050565b60006060828403121561045857600080fd5b600061046684828501610311565b91505092915050565b60006040828403121561048157600080fd5b600061048f848285016103a9565b91505092915050565b600080606083850312156104ab57600080fd5b60006104b9858286016103a9565b92505060406104ca85828601610409565b9150509250929050565b6104dd8161057f565b82525050565b6104ec8161058b565b82525050565b6104fb81610595565b82525050565b600060208201905061051660008301846104d4565b92915050565b600060208201905061053160008301846104e3565b92915050565b600060208201905061054c60008301846104f2565b92915050565b6000604051905081810181811067ffffffffffffffff8211171561057557600080fd5b8060405250919050565b60008115159050919050565b6000819050919050565b6000819050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b600063ffffffff821690509190505600a265627a7a72305820716a74dd7e2a73c237481496756750895b57977fc4876b1c48aef9b71759bf836c6578706572696d656e74616cf50037",
|
||||
"runtime_bytecode": "0x606060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063135cfdb11461006757806327e235e31461009d5780632bd14bb9146100d35780632f8086ba14610109575b600080fd5b341561007257600080fd5b6100876004610082903690610446565b61013f565b604051610094919061051c565b60405180910390f35b34156100a857600080fd5b6100bd60046100b890369061041d565b61015a565b6040516100ca9190610537565b60405180910390f35b34156100de57600080fd5b6100f360046100ee90369061046f565b610172565b6040516101009190610501565b60405180910390f35b341561011457600080fd5b6101296004610124903690610498565b6102e2565b604051610136919061051c565b60405180910390f35b6000610153826000015183602001516102e2565b9050919050565b60006020528060005260406000206000915090505481565b600081602001516000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410156101c757600090506102dd565b81602001516000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055508160200151600080846000015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540192505081905550816000015173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84602001516040516102d09190610537565b60405180910390a3600190505b919050565b60006102ed83610172565b508163ffffffff16905092915050565b6000610309823561059f565b905092915050565b60006060828403121561032357600080fd5b61032d6040610552565b9050600061033d8482850161035d565b600083015250604061035184828501610409565b60208301525092915050565b60006040828403121561036f57600080fd5b6103796040610552565b90506000610389848285016102fd565b600083015250602061039d848285016103f5565b60208301525092915050565b6000604082840312156103bb57600080fd5b6103c56040610552565b905060006103d5848285016102fd565b60008301525060206103e9848285016103f5565b60208301525092915050565b600061040182356105bf565b905092915050565b600061041582356105c9565b905092915050565b60006020828403121561042f57600080fd5b600061043d848285016102fd565b91505092915050565b60006060828403121561045857600080fd5b600061046684828501610311565b91505092915050565b60006040828403121561048157600080fd5b600061048f848285016103a9565b91505092915050565b600080606083850312156104ab57600080fd5b60006104b9858286016103a9565b92505060406104ca85828601610409565b9150509250929050565b6104dd8161057f565b82525050565b6104ec8161058b565b82525050565b6104fb81610595565b82525050565b600060208201905061051660008301846104d4565b92915050565b600060208201905061053160008301846104e3565b92915050565b600060208201905061054c60008301846104f2565b92915050565b6000604051905081810181811067ffffffffffffffff8211171561057557600080fd5b8060405250919050565b60008115159050919050565b6000819050919050565b6000819050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b600063ffffffff821690509190505600a265627a7a72305820716a74dd7e2a73c237481496756750895b57977fc4876b1c48aef9b71759bf836c6578706572696d656e74616cf50037",
|
||||
"updated_at": 1522966321930,
|
||||
"source_map": "60:1093:0:-;;;389:72;;;;;;;;449:5;426:8;:20;435:10;426:20;;;;;;;;;;;;;;;:28;;;;60:1093;;;;;;",
|
||||
"source_map_runtime": "60:1093:0:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;978:172;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;84:41;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;467:352;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;825:147;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;978:172;1051:3;1073:70;1082:18;:31;;;1115:18;:27;;;1073:8;:70::i;:::-;1066:77;;978:172;;;:::o;84:41::-;;;;;;;;;;;;;;;;;:::o;467:352::-;528:12;579;:19;;;556:8;:20;565:10;556:20;;;;;;;;;;;;;;;;:42;552:60;;;607:5;600:12;;;;552:60;646:12;:19;;;622:8;:20;631:10;622:20;;;;;;;;;;;;;;;;:43;;;;;;;;;;;704:12;:19;;;675:8;:25;684:12;:15;;;675:25;;;;;;;;;;;;;;;;:48;;;;;;;;;;;754:12;:15;;;733:58;;742:10;733:58;;;771:12;:19;;;733:58;;;;;;;;;;;;;;;808:4;801:11;;467:352;;;;:::o;825:147::-;903:3;918:22;927:12;918:8;:22::i;:::-;;957:8;950:15;;;;825:147;;;;:::o;5:118:-1:-;;72:46;110:6;97:20;72:46;;;63:55;;57:66;;;;;171:510;;294:4;282:9;277:3;273:19;269:30;266:2;;;312:1;309;302:12;266:2;330:20;345:4;330:20;;;321:29;;408:1;439:73;508:3;499:6;488:9;484:22;439:73;;;433:3;426:5;422:15;415:98;360:164;578:2;611:48;655:3;646:6;635:9;631:22;611:48;;;604:4;597:5;593:16;586:74;534:137;260:421;;;;;723:465;;836:4;824:9;819:3;815:19;811:30;808:2;;;854:1;851;844:12;808:2;872:20;887:4;872:20;;;863:29;;940:1;971:49;1016:3;1007:6;996:9;992:22;971:49;;;965:3;958:5;954:15;947:74;902:130;1084:2;1117:49;1162:3;1153:6;1142:9;1138:22;1117:49;;;1110:4;1103:5;1099:16;1092:75;1042:136;802:386;;;;;1230:469;;1347:4;1335:9;1330:3;1326:19;1322:30;1319:2;;;1365:1;1362;1355:12;1319:2;1383:20;1398:4;1383:20;;;1374:29;;1451:1;1482:49;1527:3;1518:6;1507:9;1503:22;1482:49;;;1476:3;1469:5;1465:15;1458:74;1413:130;1595:2;1628:49;1673:3;1664:6;1653:9;1649:22;1628:49;;;1621:4;1614:5;1610:16;1603:75;1553:136;1313:386;;;;;1706:118;;1773:46;1811:6;1798:20;1773:46;;;1764:55;;1758:66;;;;;1831:116;;1897:45;1934:6;1921:20;1897:45;;;1888:54;;1882:65;;;;;1954:241;;2058:2;2046:9;2037:7;2033:23;2029:32;2026:2;;;2074:1;2071;2064:12;2026:2;2109:1;2126:53;2171:7;2162:6;2151:9;2147:22;2126:53;;;2116:63;;2088:97;2020:175;;;;;2202:309;;2340:2;2328:9;2319:7;2315:23;2311:32;2308:2;;;2356:1;2353;2346:12;2308:2;2391:1;2408:87;2487:7;2478:6;2467:9;2463:22;2408:87;;;2398:97;;2370:131;2302:209;;;;;2518:297;;2650:2;2638:9;2629:7;2625:23;2621:32;2618:2;;;2666:1;2663;2656:12;2618:2;2701:1;2718:81;2791:7;2782:6;2771:9;2767:22;2718:81;;;2708:91;;2680:125;2612:203;;;;;2822:420;;;2970:2;2958:9;2949:7;2945:23;2941:32;2938:2;;;2986:1;2983;2976:12;2938:2;3021:1;3038:81;3111:7;3102:6;3091:9;3087:22;3038:81;;;3028:91;;3000:125;3156:2;3174:52;3218:7;3209:6;3198:9;3194:22;3174:52;;;3164:62;;3135:97;2932:310;;;;;;3249:101;3316:28;3338:5;3316:28;;;3311:3;3304:41;3298:52;;;3357:107;3428:30;3452:5;3428:30;;;3423:3;3416:43;3410:54;;;3471:110;3544:31;3569:5;3544:31;;;3539:3;3532:44;3526:55;;;3588:181;;3690:2;3679:9;3675:18;3667:26;;3704:55;3756:1;3745:9;3741:17;3732:6;3704:55;;;3661:108;;;;;3776:189;;3882:2;3871:9;3867:18;3859:26;;3896:59;3952:1;3941:9;3937:17;3928:6;3896:59;;;3853:112;;;;;3972:193;;4080:2;4069:9;4065:18;4057:26;;4094:61;4152:1;4141:9;4137:17;4128:6;4094:61;;;4051:114;;;;;4172:256;;4234:2;4228:9;4218:19;;4272:4;4264:6;4260:17;4371:6;4359:10;4356:22;4335:18;4323:10;4320:34;4317:62;4314:2;;;4392:1;4389;4382:12;4314:2;4412:10;4408:2;4401:22;4212:216;;;;;4435:92;;4515:5;4508:13;4501:21;4490:32;;4484:43;;;;4534:78;;4602:5;4591:16;;4585:27;;;;4619:79;;4688:5;4677:16;;4671:27;;;;4705:128;;4785:42;4778:5;4774:54;4763:65;;4757:76;;;;4840:79;;4909:5;4898:16;;4892:27;;;;4926:95;;5005:10;4998:5;4994:22;4983:33;;4977:44;;;",
|
||||
"sources": [
|
||||
"/Metacoin.sol"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -11,6 +11,11 @@ contract Metacoin {
|
||||
uint256 amount;
|
||||
}
|
||||
|
||||
struct NestedTransferData {
|
||||
TransferData transferData;
|
||||
uint32 callback;
|
||||
}
|
||||
|
||||
function Metacoin() public {
|
||||
balances[msg.sender] = 10000;
|
||||
}
|
||||
@ -22,4 +27,14 @@ contract Metacoin {
|
||||
Transfer(msg.sender, transferData.to, transferData.amount);
|
||||
return true;
|
||||
}
|
||||
|
||||
function transfer(TransferData transferData, uint32 callback) public returns (int) {
|
||||
transfer(transferData);
|
||||
return callback;
|
||||
}
|
||||
|
||||
function transfer(NestedTransferData nestedTransferData) public returns (int) {
|
||||
return transfer(nestedTransferData.transferData, nestedTransferData.callback);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -18,7 +18,7 @@
|
||||
"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 --contracts Metacoin --contract-dirs contracts --artifacts-dir artifacts"
|
||||
},
|
||||
"author": "",
|
||||
"license": "Apache-2.0",
|
||||
|
@ -36,12 +36,12 @@ describe('Metacoin', () => {
|
||||
});
|
||||
});
|
||||
describe('#transfer', () => {
|
||||
it(`should successfully transfer tokens`, async () => {
|
||||
it(`should successfully transfer tokens (via transfer_1)`, async () => {
|
||||
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
|
||||
const amount = INITIAL_BALANCE.div(2);
|
||||
const oldBalance = await metacoin.balances.callAsync(ZERO_ADDRESS);
|
||||
expect(oldBalance).to.be.bignumber.equal(0);
|
||||
const txHash = await metacoin.transfer.sendTransactionAsync(
|
||||
const txHash = await metacoin.transfer_1.sendTransactionAsync(
|
||||
{
|
||||
to: ZERO_ADDRESS,
|
||||
amount,
|
||||
@ -58,5 +58,57 @@ describe('Metacoin', () => {
|
||||
const newBalance = await metacoin.balances.callAsync(ZERO_ADDRESS);
|
||||
expect(newBalance).to.be.bignumber.equal(amount);
|
||||
});
|
||||
|
||||
it(`should successfully transfer tokens (via transfer_2)`, async () => {
|
||||
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
|
||||
const amount = INITIAL_BALANCE.div(2);
|
||||
const oldBalance = await metacoin.balances.callAsync(ZERO_ADDRESS);
|
||||
expect(oldBalance).to.be.bignumber.equal(0);
|
||||
const callback = 59;
|
||||
const txHash = await metacoin.transfer_2.sendTransactionAsync(
|
||||
{
|
||||
to: ZERO_ADDRESS,
|
||||
amount,
|
||||
},
|
||||
callback,
|
||||
{ from: devConstants.TESTRPC_FIRST_ADDRESS },
|
||||
);
|
||||
const txReceipt = await web3Wrapper.awaitTransactionMinedAsync(txHash);
|
||||
const transferLogs = txReceipt.logs[0] as LogWithDecodedArgs<TransferContractEventArgs>;
|
||||
expect(transferLogs.args).to.be.deep.equal({
|
||||
_to: ZERO_ADDRESS,
|
||||
_from: devConstants.TESTRPC_FIRST_ADDRESS,
|
||||
_value: amount,
|
||||
});
|
||||
const newBalance = await metacoin.balances.callAsync(ZERO_ADDRESS);
|
||||
expect(newBalance).to.be.bignumber.equal(amount);
|
||||
});
|
||||
|
||||
it(`should successfully transfer tokens (via transfer_3)`, async () => {
|
||||
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
|
||||
const amount = INITIAL_BALANCE.div(2);
|
||||
const oldBalance = await metacoin.balances.callAsync(ZERO_ADDRESS);
|
||||
expect(oldBalance).to.be.bignumber.equal(0);
|
||||
const callback = 59;
|
||||
const txHash = await metacoin.transfer_3.sendTransactionAsync(
|
||||
{
|
||||
transferData: {
|
||||
to: ZERO_ADDRESS,
|
||||
amount,
|
||||
},
|
||||
callback,
|
||||
},
|
||||
{ from: devConstants.TESTRPC_FIRST_ADDRESS },
|
||||
);
|
||||
const txReceipt = await web3Wrapper.awaitTransactionMinedAsync(txHash);
|
||||
const transferLogs = txReceipt.logs[0] as LogWithDecodedArgs<TransferContractEventArgs>;
|
||||
expect(transferLogs.args).to.be.deep.equal({
|
||||
_to: ZERO_ADDRESS,
|
||||
_from: devConstants.TESTRPC_FIRST_ADDRESS,
|
||||
_value: amount,
|
||||
});
|
||||
const newBalance = await metacoin.balances.callAsync(ZERO_ADDRESS);
|
||||
expect(newBalance).to.be.bignumber.equal(amount);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
74
packages/utils/src/abi_utils.ts
Normal file
74
packages/utils/src/abi_utils.ts
Normal file
@ -0,0 +1,74 @@
|
||||
import { AbiDefinition, AbiType, ConstructorAbi, ContractAbi, DataItem, MethodAbi } from '@0xproject/types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
export const abiUtils = {
|
||||
parseFunctionParam(param: DataItem): string {
|
||||
if (param.type === 'tuple') {
|
||||
// Parse out tuple types into {type_1, type_2, ..., type_N}
|
||||
const tupleComponents = param.components;
|
||||
const paramString = _.map(tupleComponents, component => this.parseFunctionParam(component));
|
||||
const tupleParamString = `{${paramString}}`;
|
||||
return tupleParamString;
|
||||
}
|
||||
return param.type;
|
||||
},
|
||||
getFunctionSignature(abi: MethodAbi): string {
|
||||
const functionName = abi.name;
|
||||
const parameterTypeList = abi.inputs.map((param: DataItem) => this.parseFunctionParam(param));
|
||||
const functionSignature = `${functionName}(${parameterTypeList})`;
|
||||
return functionSignature;
|
||||
},
|
||||
renameOverloadedMethods(inputContractAbi: ContractAbi): ContractAbi {
|
||||
const contractAbi = _.cloneDeep(inputContractAbi);
|
||||
const methodAbis = contractAbi.filter((abi: AbiDefinition) => abi.type === AbiType.Function) as MethodAbi[];
|
||||
const methodAbisByOriginalIndex = _.transform(
|
||||
methodAbis,
|
||||
(result: Array<{ index: number; methodAbi: MethodAbi }>, methodAbi, i: number) => {
|
||||
result.push({ index: i, methodAbi });
|
||||
},
|
||||
[],
|
||||
);
|
||||
// Sort method Abis into alphabetical order, by function signature
|
||||
const methodAbisByOriginalIndexOrdered = _.sortBy(methodAbisByOriginalIndex, [
|
||||
(entry: { index: number; methodAbi: MethodAbi }) => {
|
||||
const functionSignature = this.getFunctionSignature(entry.methodAbi);
|
||||
return functionSignature;
|
||||
},
|
||||
]);
|
||||
// Group method Abis by name (overloaded methods will be grouped together, in alphabetical order)
|
||||
const methodAbisByName = _.transform(
|
||||
methodAbisByOriginalIndexOrdered,
|
||||
(result: { [key: string]: Array<{ index: number; methodAbi: MethodAbi }> }, entry) => {
|
||||
(result[entry.methodAbi.name] || (result[entry.methodAbi.name] = [])).push(entry);
|
||||
},
|
||||
{},
|
||||
);
|
||||
// Rename overloaded methods to overloadedMethoName_1, overloadedMethoName_2, ...
|
||||
const methodAbisRenamed = _.transform(
|
||||
methodAbisByName,
|
||||
(result: MethodAbi[], methodAbisWithSameName: Array<{ index: number; methodAbi: MethodAbi }>) => {
|
||||
_.forEach(methodAbisWithSameName, (entry, i: number) => {
|
||||
if (methodAbisWithSameName.length > 1) {
|
||||
const overloadedMethodId = i + 1;
|
||||
const sanitizedMethodName = `${entry.methodAbi.name}_${overloadedMethodId}`;
|
||||
const indexOfExistingAbiWithSanitizedMethodNameIfExists = _.findIndex(
|
||||
methodAbis,
|
||||
methodAbi => methodAbi.name === sanitizedMethodName,
|
||||
);
|
||||
if (indexOfExistingAbiWithSanitizedMethodNameIfExists >= 0) {
|
||||
const methodName = entry.methodAbi.name;
|
||||
throw new Error(
|
||||
`Failed to rename overloaded method '${methodName}' to '${sanitizedMethodName}'. A method with this name already exists.`,
|
||||
);
|
||||
}
|
||||
entry.methodAbi.name = sanitizedMethodName;
|
||||
}
|
||||
// Add method to list of ABIs in its original position
|
||||
result.splice(entry.index, 0, entry.methodAbi);
|
||||
});
|
||||
},
|
||||
[...Array(methodAbis.length)],
|
||||
);
|
||||
return contractAbi;
|
||||
},
|
||||
};
|
@ -5,3 +5,4 @@ export { intervalUtils } from './interval_utils';
|
||||
export { BigNumber } from './configured_bignumber';
|
||||
export { AbiDecoder } from './abi_decoder';
|
||||
export { logUtils } from './log_utils';
|
||||
export { abiUtils } from './abi_utils';
|
||||
|
101
yarn.lock
101
yarn.lock
@ -6,6 +6,41 @@
|
||||
version "0.3.9"
|
||||
resolved "https://registry.yarnpkg.com/8fold-marked/-/8fold-marked-0.3.9.tgz#bb89c645612f8ccfaffac1ca6e3c11f168c9cf59"
|
||||
|
||||
"@0xproject/dev-utils@^0.2.1":
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@0xproject/dev-utils/-/dev-utils-0.2.1.tgz#a54465376fd7c8cf58781b02b1790d74fb51e91b"
|
||||
dependencies:
|
||||
"@0xproject/subproviders" "^0.7.0"
|
||||
"@0xproject/types" "^0.3.1"
|
||||
"@0xproject/utils" "^0.4.1"
|
||||
ethereumjs-util "^5.1.2"
|
||||
lodash "^4.17.4"
|
||||
request-promise-native "^1.0.5"
|
||||
web3 "^0.20.0"
|
||||
web3-provider-engine "^13.0.1"
|
||||
|
||||
"@0xproject/subproviders@^0.7.0":
|
||||
version "0.7.0"
|
||||
resolved "https://registry.yarnpkg.com/@0xproject/subproviders/-/subproviders-0.7.0.tgz#ce3379a85649773e5c08f5fc3239e8ed07f13361"
|
||||
dependencies:
|
||||
"@0xproject/assert" "^0.2.0"
|
||||
"@0xproject/types" "^0.3.1"
|
||||
"@0xproject/utils" "^0.4.1"
|
||||
"@ledgerhq/hw-app-eth" "^4.3.0"
|
||||
"@ledgerhq/hw-transport-u2f" "^4.3.0"
|
||||
bn.js "^4.11.8"
|
||||
es6-promisify "^5.0.0"
|
||||
ethereumjs-tx "^1.3.3"
|
||||
ethereumjs-util "^5.1.1"
|
||||
hdkey "^0.7.1"
|
||||
lodash "^4.17.4"
|
||||
semaphore-async-await "^1.5.1"
|
||||
web3 "^0.20.0"
|
||||
web3-provider-engine "^13.0.1"
|
||||
web3-typescript-typings "^0.10.0"
|
||||
optionalDependencies:
|
||||
"@ledgerhq/hw-transport-node-hid" "^4.3.0"
|
||||
|
||||
"@0xproject/tslint-config@0.4.13":
|
||||
version "0.4.13"
|
||||
resolved "https://registry.yarnpkg.com/@0xproject/tslint-config/-/tslint-config-0.4.13.tgz#98c71c5ae5e80315a23eda0134cc9f6f4438cac2"
|
||||
@ -15,6 +50,40 @@
|
||||
tslint-eslint-rules "^4.1.1"
|
||||
tslint-react "^3.2.0"
|
||||
|
||||
"@0xproject/types@^0.3.1":
|
||||
version "0.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@0xproject/types/-/types-0.3.1.tgz#9a75be6d3a2d41b7ecbd9105c3fdc09f3e3ec297"
|
||||
dependencies:
|
||||
bignumber.js "~4.1.0"
|
||||
web3 "^0.20.0"
|
||||
web3-typescript-typings "^0.10.0"
|
||||
|
||||
"@0xproject/types@^0.4.2":
|
||||
version "0.4.2"
|
||||
resolved "https://registry.yarnpkg.com/@0xproject/types/-/types-0.4.2.tgz#83d6ebef60f41e6209acb2656b1d68ff79367ca5"
|
||||
dependencies:
|
||||
bignumber.js "~4.1.0"
|
||||
|
||||
"@0xproject/typescript-typings@^0.0.2":
|
||||
version "0.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@0xproject/typescript-typings/-/typescript-typings-0.0.2.tgz#b549ea3c81ce2d81b99f05583bdf7c411a3ca90c"
|
||||
dependencies:
|
||||
"@0xproject/types" "^0.4.2"
|
||||
bignumber.js "~4.1.0"
|
||||
|
||||
"@0xproject/utils@^0.4.1":
|
||||
version "0.4.4"
|
||||
resolved "https://registry.yarnpkg.com/@0xproject/utils/-/utils-0.4.4.tgz#bce4f7a5a46570a69911f4a4ade5d49016330087"
|
||||
dependencies:
|
||||
"@0xproject/types" "^0.4.2"
|
||||
"@0xproject/typescript-typings" "^0.0.2"
|
||||
"@types/node" "^8.0.53"
|
||||
bignumber.js "~4.1.0"
|
||||
ethers-contracts "^2.2.1"
|
||||
js-sha3 "^0.7.0"
|
||||
lodash "^4.17.4"
|
||||
web3 "^0.20.0"
|
||||
|
||||
"@ledgerhq/hw-app-eth@^4.3.0":
|
||||
version "4.7.3"
|
||||
resolved "https://registry.yarnpkg.com/@ledgerhq/hw-app-eth/-/hw-app-eth-4.7.3.tgz#d352e19658ae296532e522c53c8ec2a1a77b64e5"
|
||||
@ -285,7 +354,13 @@
|
||||
dependencies:
|
||||
redux "*"
|
||||
|
||||
"@types/request@2.47.0":
|
||||
"@types/request-promise-native@^1.0.2":
|
||||
version "1.0.14"
|
||||
resolved "https://registry.yarnpkg.com/@types/request-promise-native/-/request-promise-native-1.0.14.tgz#20f2ba136e6f29c2ea745c60767738d434793d31"
|
||||
dependencies:
|
||||
"@types/request" "*"
|
||||
|
||||
"@types/request@*", "@types/request@2.47.0":
|
||||
version "2.47.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/request/-/request-2.47.0.tgz#76a666cee4cb85dcffea6cd4645227926d9e114e"
|
||||
dependencies:
|
||||
@ -3562,7 +3637,7 @@ ethereumjs-util@^4.0.1, ethereumjs-util@^4.3.0, ethereumjs-util@^4.4.0:
|
||||
rlp "^2.0.0"
|
||||
secp256k1 "^3.0.1"
|
||||
|
||||
ethereumjs-util@^5.0.0, ethereumjs-util@^5.0.1, ethereumjs-util@^5.1.1, ethereumjs-util@^5.1.3, ethereumjs-util@^5.1.5:
|
||||
ethereumjs-util@^5.0.0, ethereumjs-util@^5.0.1, ethereumjs-util@^5.1.1, ethereumjs-util@^5.1.2, ethereumjs-util@^5.1.3, ethereumjs-util@^5.1.5:
|
||||
version "5.1.5"
|
||||
resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-5.1.5.tgz#2f02575852627d45622426f25ee4a0b5f377f27a"
|
||||
dependencies:
|
||||
@ -8870,6 +8945,20 @@ request-ip@~1.2.3:
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/request-ip/-/request-ip-1.2.3.tgz#66988f0e22406ec4af630d19b573fe4b447c3b49"
|
||||
|
||||
request-promise-core@1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.1.tgz#3eee00b2c5aa83239cfb04c5700da36f81cd08b6"
|
||||
dependencies:
|
||||
lodash "^4.13.1"
|
||||
|
||||
request-promise-native@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.5.tgz#5281770f68e0c9719e5163fd3fab482215f4fda5"
|
||||
dependencies:
|
||||
request-promise-core "1.1.1"
|
||||
stealthy-require "^1.1.0"
|
||||
tough-cookie ">=2.3.3"
|
||||
|
||||
request@2.81.0:
|
||||
version "2.81.0"
|
||||
resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0"
|
||||
@ -9751,6 +9840,10 @@ static-extend@^0.1.1:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087"
|
||||
|
||||
stealthy-require@^1.1.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b"
|
||||
|
||||
stream-browserify@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db"
|
||||
@ -10323,7 +10416,7 @@ touch@^3.1.0:
|
||||
dependencies:
|
||||
nopt "~1.0.10"
|
||||
|
||||
tough-cookie@~2.3.0, tough-cookie@~2.3.3:
|
||||
tough-cookie@>=2.3.3, tough-cookie@~2.3.0, tough-cookie@~2.3.3:
|
||||
version "2.3.4"
|
||||
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.4.tgz#ec60cee38ac675063ffc97a5c18970578ee83655"
|
||||
dependencies:
|
||||
@ -11267,7 +11360,7 @@ web3-shh@1.0.0-beta.33:
|
||||
web3-core-subscriptions "1.0.0-beta.33"
|
||||
web3-net "1.0.0-beta.33"
|
||||
|
||||
web3-typescript-typings@^0.10.2:
|
||||
web3-typescript-typings@^0.10.0, web3-typescript-typings@^0.10.2:
|
||||
version "0.10.2"
|
||||
resolved "https://registry.yarnpkg.com/web3-typescript-typings/-/web3-typescript-typings-0.10.2.tgz#a9903815d2a8a0dbd73fd5db374070de0bd30497"
|
||||
dependencies:
|
||||
|
Loading…
x
Reference in New Issue
Block a user