From 4b3e0383235ca4ca0127f24c2e05543bb45a56bb Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Wed, 29 Nov 2017 22:02:43 -0800 Subject: Add contracts to packages, fix most linting errors --- packages/contracts/deploy/src/utils/constants.ts | 3 + packages/contracts/deploy/src/utils/contract.ts | 81 +++++++++++++ packages/contracts/deploy/src/utils/encoder.ts | 20 ++++ packages/contracts/deploy/src/utils/fs_wrapper.ts | 11 ++ packages/contracts/deploy/src/utils/network.ts | 15 +++ packages/contracts/deploy/src/utils/types.ts | 95 +++++++++++++++ packages/contracts/deploy/src/utils/utils.ts | 13 ++ .../contracts/deploy/src/utils/web3_wrapper.ts | 132 +++++++++++++++++++++ 8 files changed, 370 insertions(+) create mode 100644 packages/contracts/deploy/src/utils/constants.ts create mode 100644 packages/contracts/deploy/src/utils/contract.ts create mode 100644 packages/contracts/deploy/src/utils/encoder.ts create mode 100644 packages/contracts/deploy/src/utils/fs_wrapper.ts create mode 100644 packages/contracts/deploy/src/utils/network.ts create mode 100644 packages/contracts/deploy/src/utils/types.ts create mode 100644 packages/contracts/deploy/src/utils/utils.ts create mode 100644 packages/contracts/deploy/src/utils/web3_wrapper.ts (limited to 'packages/contracts/deploy/src/utils') diff --git a/packages/contracts/deploy/src/utils/constants.ts b/packages/contracts/deploy/src/utils/constants.ts new file mode 100644 index 000000000..8871a470d --- /dev/null +++ b/packages/contracts/deploy/src/utils/constants.ts @@ -0,0 +1,3 @@ +export const constants = { + NULL_BYTES: '0x', +}; diff --git a/packages/contracts/deploy/src/utils/contract.ts b/packages/contracts/deploy/src/utils/contract.ts new file mode 100644 index 000000000..e9c49c9f1 --- /dev/null +++ b/packages/contracts/deploy/src/utils/contract.ts @@ -0,0 +1,81 @@ +import {schemas, SchemaValidator} from '@0xproject/json-schemas'; +import promisify = require('es6-promisify'); +import * as _ from 'lodash'; +import * as Web3 from 'web3'; + +import {AbiType} from './types'; + +export class Contract implements Web3.ContractInstance { + public address: string; + public abi: Web3.ContractAbi; + private contract: Web3.ContractInstance; + private defaults: Partial; + private validator: SchemaValidator; + // This class instance is going to be populated with functions and events depending on the ABI + // and we don't know their types in advance + [name: string]: any; + constructor(web3ContractInstance: Web3.ContractInstance, defaults: Partial) { + this.contract = web3ContractInstance; + this.address = web3ContractInstance.address; + this.abi = web3ContractInstance.abi; + this.defaults = defaults; + this.populateEvents(); + this.populateFunctions(); + this.validator = new SchemaValidator(); + } + private populateFunctions(): void { + const functionsAbi = _.filter(this.abi, abiPart => abiPart.type === AbiType.Function); + _.forEach(functionsAbi, (functionAbi: Web3.MethodAbi) => { + if (functionAbi.constant) { + const cbStyleCallFunction = this.contract[functionAbi.name].call; + this[functionAbi.name] = { + callAsync: promisify(cbStyleCallFunction, this.contract), + }; + } else { + const cbStyleFunction = this.contract[functionAbi.name]; + const cbStyleEstimateGasFunction = this.contract[functionAbi.name].estimateGas; + this[functionAbi.name] = { + estimateGasAsync: promisify(cbStyleEstimateGasFunction, this.contract), + sendTransactionAsync: this.promisifyWithDefaultParams(cbStyleFunction), + }; + } + }); + } + private populateEvents(): void { + const eventsAbi = _.filter(this.abi, abiPart => abiPart.type === AbiType.Event); + _.forEach(eventsAbi, (eventAbi: Web3.EventAbi) => { + this[eventAbi.name] = this.contract[eventAbi.name]; + }); + } + private promisifyWithDefaultParams(fn: (...args: any[]) => void): (...args: any[]) => Promise { + const promisifiedWithDefaultParams = async (...args: any[]) => { + const promise = new Promise((resolve, reject) => { + const lastArg = args[args.length - 1]; + let txData: Partial = {}; + if (this.isTxData(lastArg)) { + txData = args.pop(); + } + txData = { + ...this.defaults, + ...txData, + }; + const callback = (err: Error, data: any) => { + if (_.isNull(err)) { + resolve(data); + } else { + reject(err); + } + }; + args.push(txData); + args.push(callback); + fn.apply(this.contract, args); + }); + return promise; + }; + return promisifiedWithDefaultParams; + } + private isTxData(lastArg: any): boolean { + const isValid = this.validator.isValid(lastArg, schemas.txDataSchema); + return isValid; + } +} diff --git a/packages/contracts/deploy/src/utils/encoder.ts b/packages/contracts/deploy/src/utils/encoder.ts new file mode 100644 index 000000000..0248e9f03 --- /dev/null +++ b/packages/contracts/deploy/src/utils/encoder.ts @@ -0,0 +1,20 @@ +import * as _ from 'lodash'; +import * as Web3 from 'web3'; +import * as web3Abi from 'web3-eth-abi'; + +import {AbiType} from './types'; + +export const encoder = { + encodeConstructorArgsFromAbi(args: any[], abi: Web3.ContractAbi): string { + const constructorTypes: string[] = []; + _.each(abi, (element: Web3.AbiDefinition) => { + if (element.type === AbiType.Constructor) { + _.each(element.inputs, (input: Web3.FunctionParameter) => { + constructorTypes.push(input.type); + }); + } + }); + const encodedParameters = web3Abi.encodeParameters(constructorTypes, args); + return encodedParameters; + }, +}; diff --git a/packages/contracts/deploy/src/utils/fs_wrapper.ts b/packages/contracts/deploy/src/utils/fs_wrapper.ts new file mode 100644 index 000000000..6b4fd625c --- /dev/null +++ b/packages/contracts/deploy/src/utils/fs_wrapper.ts @@ -0,0 +1,11 @@ +import promisify = require('es6-promisify'); +import * as fs from 'fs'; + +export const fsWrapper = { + readdirAsync: promisify(fs.readdir), + readFileAsync: promisify(fs.readFile), + writeFileAsync: promisify(fs.writeFile), + mkdirAsync: promisify(fs.mkdir), + doesPathExistSync: fs.existsSync, + removeFileAsync: promisify(fs.unlink), +}; diff --git a/packages/contracts/deploy/src/utils/network.ts b/packages/contracts/deploy/src/utils/network.ts new file mode 100644 index 000000000..74123e6a5 --- /dev/null +++ b/packages/contracts/deploy/src/utils/network.ts @@ -0,0 +1,15 @@ +import promisify = require('es6-promisify'); +import * as Web3 from 'web3'; + +import {Web3Wrapper} from './web3_wrapper'; + +export const network = { + async getNetworkIdIfExistsAsync(port: number): Promise { + const url = `http://localhost:${port}`; + const web3Provider = new Web3.providers.HttpProvider(url); + const defaults = {}; + const web3Wrapper = new Web3Wrapper(web3Provider, defaults); + const networkIdIfExists = await web3Wrapper.getNetworkIdIfExistsAsync(); + return networkIdIfExists; + }, +}; diff --git a/packages/contracts/deploy/src/utils/types.ts b/packages/contracts/deploy/src/utils/types.ts new file mode 100644 index 000000000..855f1e849 --- /dev/null +++ b/packages/contracts/deploy/src/utils/types.ts @@ -0,0 +1,95 @@ +import * as Web3 from 'web3'; + +export enum AbiType { + Function = 'function', + Constructor = 'constructor', + Event = 'event', + Fallback = 'fallback', +} + +export interface ContractArtifact { + contract_name: string; + networks: ContractNetworks; +} + +export interface ContractNetworks { + [key: number]: ContractData; +} + +export interface ContractData { + solc_version: string; + optimizer_enabled: number; + keccak256: string; + abi: Web3.ContractAbi; + unlinked_binary: string; + address?: string; + constructor_args?: string; + updated_at: number; +} + +export interface SolcErrors { + [key: string]: boolean; +} + +export interface CliOptions { + artifactsDir: string; + contractsDir: string; + jsonrpcPort: number; + networkId: number; + shouldOptimize: boolean; + gasPrice: string; + account?: string; + contract?: string; + args?: string; +} + +export interface CompilerOptions { + contractsDir: string; + networkId: number; + optimizerEnabled: number; + artifactsDir: string; +} + +export interface DeployerOptions { + artifactsDir: string; + jsonrpcPort: number; + networkId: number; + defaults: Partial; +} + +export interface ContractSources { + [key: string]: string; +} + +export interface ImportContents { + contents: string; +} + +// TODO: Consolidate with 0x.js definitions once types are moved into a separate package. +export enum ZeroExError { + ContractDoesNotExist = 'CONTRACT_DOES_NOT_EXIST', + ExchangeContractDoesNotExist = 'EXCHANGE_CONTRACT_DOES_NOT_EXIST', + UnhandledError = 'UNHANDLED_ERROR', + UserHasNoAssociatedAddress = 'USER_HAS_NO_ASSOCIATED_ADDRESSES', + InvalidSignature = 'INVALID_SIGNATURE', + ContractNotDeployedOnNetwork = 'CONTRACT_NOT_DEPLOYED_ON_NETWORK', + InsufficientAllowanceForTransfer = 'INSUFFICIENT_ALLOWANCE_FOR_TRANSFER', + InsufficientBalanceForTransfer = 'INSUFFICIENT_BALANCE_FOR_TRANSFER', + InsufficientEthBalanceForDeposit = 'INSUFFICIENT_ETH_BALANCE_FOR_DEPOSIT', + InsufficientWEthBalanceForWithdrawal = 'INSUFFICIENT_WETH_BALANCE_FOR_WITHDRAWAL', + InvalidJump = 'INVALID_JUMP', + OutOfGas = 'OUT_OF_GAS', + NoNetworkId = 'NO_NETWORK_ID', + SubscriptionNotFound = 'SUBSCRIPTION_NOT_FOUND', +} + +export interface Token { + address?: string; + name: string; + symbol: string; + decimals: number; + ipfsHash: string; + swarmHash: string; +} + +export type DoneCallback = (err?: Error) => void; diff --git a/packages/contracts/deploy/src/utils/utils.ts b/packages/contracts/deploy/src/utils/utils.ts new file mode 100644 index 000000000..4390d8813 --- /dev/null +++ b/packages/contracts/deploy/src/utils/utils.ts @@ -0,0 +1,13 @@ +export const utils = { + consoleLog(message: string): void { + /* tslint:disable */ + console.log(message); + /* tslint:enable */ + }, + stringifyWithFormatting(obj: any): string { + const jsonReplacer: null = null; + const numberOfJsonSpaces = 4; + const stringifiedObj = JSON.stringify(obj, jsonReplacer, numberOfJsonSpaces); + return stringifiedObj; + }, +}; diff --git a/packages/contracts/deploy/src/utils/web3_wrapper.ts b/packages/contracts/deploy/src/utils/web3_wrapper.ts new file mode 100644 index 000000000..0209da26d --- /dev/null +++ b/packages/contracts/deploy/src/utils/web3_wrapper.ts @@ -0,0 +1,132 @@ +import BigNumber from 'bignumber.js'; +import promisify = require('es6-promisify'); +import * as _ from 'lodash'; +import * as Web3 from 'web3'; + +import {Contract} from './contract'; +import {ZeroExError} from './types'; + +export class Web3Wrapper { + private web3: Web3; + private defaults: Partial; + private networkIdIfExists?: number; + private jsonRpcRequestId: number; + constructor(provider: Web3.Provider, defaults: Partial) { + this.web3 = new Web3(); + this.web3.setProvider(provider); + this.defaults = defaults; + this.jsonRpcRequestId = 0; + } + public setProvider(provider: Web3.Provider) { + delete this.networkIdIfExists; + this.web3.setProvider(provider); + } + public isAddress(address: string): boolean { + return this.web3.isAddress(address); + } + public getContractFromAbi(abi: Web3.ContractAbi): Web3.Contract { + const contract = this.web3.eth.contract(abi); + return contract; + } + public async isSenderAddressAvailableAsync(senderAddress: string): Promise { + const addresses = await this.getAvailableAddressesAsync(); + return _.includes(addresses, senderAddress); + } + public async getNodeVersionAsync(): Promise { + const nodeVersion = await promisify(this.web3.version.getNode)(); + return nodeVersion; + } + public async getTransactionReceiptAsync(txHash: string): Promise { + const transactionReceipt = await promisify(this.web3.eth.getTransactionReceipt)(txHash); + return transactionReceipt; + } + public getCurrentProvider(): Web3.Provider { + return this.web3.currentProvider; + } + public async getNetworkIdIfExistsAsync(): Promise { + if (!_.isUndefined(this.networkIdIfExists)) { + return this.networkIdIfExists; + } + + try { + const networkId = await this.getNetworkAsync(); + this.networkIdIfExists = Number(networkId); + return this.networkIdIfExists; + } catch (err) { + return undefined; + } + } + public toWei(ethAmount: BigNumber): BigNumber { + const balanceWei = this.web3.toWei(ethAmount, 'ether'); + return balanceWei; + } + public async getBalanceInWeiAsync(owner: string): Promise { + let balanceInWei = await promisify(this.web3.eth.getBalance)(owner); + balanceInWei = new BigNumber(balanceInWei); + return balanceInWei; + } + public async doesContractExistAtAddressAsync(address: string): Promise { + const code = await promisify(this.web3.eth.getCode)(address); + // Regex matches 0x0, 0x00, 0x in order to accommodate poorly implemented clients + const codeIsEmpty = /^0x0{0,40}$/i.test(code); + return !codeIsEmpty; + } + public async signTransactionAsync(address: string, message: string): Promise { + const signData = await promisify(this.web3.eth.sign)(address, message); + return signData; + } + public async getBlockAsync(blockParam: string|Web3.BlockParam): Promise { + const block = await promisify(this.web3.eth.getBlock)(blockParam); + return block; + } + public async getBlockTimestampAsync(blockParam: string|Web3.BlockParam): Promise { + const {timestamp} = await this.getBlockAsync(blockParam); + return timestamp; + } + public async getAvailableAddressesAsync(): Promise { + const addresses: string[] = await promisify(this.web3.eth.getAccounts)(); + return addresses; + } + public async getLogsAsync(filter: Web3.FilterObject): Promise { + let fromBlock = filter.fromBlock; + if (_.isNumber(fromBlock)) { + fromBlock = this.web3.toHex(fromBlock); + } + let toBlock = filter.toBlock; + if (_.isNumber(toBlock)) { + toBlock = this.web3.toHex(toBlock); + } + const serializedFilter = { + ...filter, + fromBlock, + toBlock, + }; + const payload = { + jsonrpc: '2.0', + id: this.jsonRpcRequestId++, + method: 'eth_getLogs', + params: [serializedFilter], + }; + const logs = await this.sendRawPayloadAsync(payload); + return logs; + } + public async estimateGasAsync(callData: Web3.CallData): Promise { + const gasEstimate = await promisify(this.web3.eth.estimateGas)(callData); + return gasEstimate; + } + private getContractInstance(abi: Web3.ContractAbi, address: string): A { + const web3ContractInstance = this.web3.eth.contract(abi).at(address); + const contractInstance = new Contract(web3ContractInstance, this.defaults) as any as A; + return contractInstance; + } + private async getNetworkAsync(): Promise { + const networkId = await promisify(this.web3.version.getNetwork)(); + return networkId; + } + private async sendRawPayloadAsync(payload: Web3.JSONRPCRequestPayload): Promise { + const sendAsync = this.web3.currentProvider.sendAsync.bind(this.web3.currentProvider); + const response = await promisify(sendAsync)(payload); + const result = response.result; + return result; + } +} -- cgit v1.2.3