diff options
Diffstat (limited to 'packages/contracts/src/utils')
19 files changed, 1227 insertions, 0 deletions
diff --git a/packages/contracts/src/utils/address_utils.ts b/packages/contracts/src/utils/address_utils.ts new file mode 100644 index 000000000..01a7a6fd4 --- /dev/null +++ b/packages/contracts/src/utils/address_utils.ts @@ -0,0 +1,12 @@ +import { ZeroEx } from '0x.js'; + +import { crypto } from './crypto'; + +export const addressUtils = { + generatePseudoRandomAddress(): string { + const randomBigNum = ZeroEx.generatePseudoRandomSalt(); + const randomBuff = crypto.solSHA3([randomBigNum]); + const randomAddress = `0x${randomBuff.slice(0, 20).toString('hex')}`; + return randomAddress; + }, +}; diff --git a/packages/contracts/src/utils/artifacts.ts b/packages/contracts/src/utils/artifacts.ts new file mode 100644 index 000000000..caf5b94fd --- /dev/null +++ b/packages/contracts/src/utils/artifacts.ts @@ -0,0 +1,37 @@ +import { ContractArtifact } from '@0xproject/sol-compiler'; + +import * as DummyERC20Token from '../artifacts/DummyERC20Token.json'; +import * as DummyERC721Token from '../artifacts/DummyERC721Token.json'; +import * as ERC20Proxy from '../artifacts/ERC20Proxy.json'; +import * as ERC721Proxy from '../artifacts/ERC721Proxy.json'; +import * as Exchange from '../artifacts/Exchange.json'; +import * as MixinAuthorizable from '../artifacts/MixinAuthorizable.json'; +import * as MultiSigWallet from '../artifacts/MultiSigWallet.json'; +import * as MultiSigWalletWithTimeLock from '../artifacts/MultiSigWalletWithTimeLock.json'; +import * as MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress from '../artifacts/MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress.json'; +import * as TestAssetProxyDispatcher from '../artifacts/TestAssetProxyDispatcher.json'; +import * as TestLibBytes from '../artifacts/TestLibBytes.json'; +import * as TestLibs from '../artifacts/TestLibs.json'; +import * as TestSignatureValidator from '../artifacts/TestSignatureValidator.json'; +import * as TokenRegistry from '../artifacts/TokenRegistry.json'; +import * as EtherToken from '../artifacts/WETH9.json'; +import * as ZRX from '../artifacts/ZRXToken.json'; + +export const artifacts = { + DummyERC20Token: (DummyERC20Token as any) as ContractArtifact, + DummyERC721Token: (DummyERC721Token as any) as ContractArtifact, + ERC20Proxy: (ERC20Proxy as any) as ContractArtifact, + ERC721Proxy: (ERC721Proxy as any) as ContractArtifact, + Exchange: (Exchange as any) as ContractArtifact, + EtherToken: (EtherToken as any) as ContractArtifact, + MixinAuthorizable: (MixinAuthorizable as any) as ContractArtifact, + MultiSigWallet: (MultiSigWallet as any) as ContractArtifact, + MultiSigWalletWithTimeLock: (MultiSigWalletWithTimeLock as any) as ContractArtifact, + MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress: (MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress as any) as ContractArtifact, + TestAssetProxyDispatcher: (TestAssetProxyDispatcher as any) as ContractArtifact, + TestLibBytes: (TestLibBytes as any) as ContractArtifact, + TestLibs: (TestLibs as any) as ContractArtifact, + TestSignatureValidator: (TestSignatureValidator as any) as ContractArtifact, + TokenRegistry: (TokenRegistry as any) as ContractArtifact, + ZRX: (ZRX as any) as ContractArtifact, +}; diff --git a/packages/contracts/src/utils/asset_proxy_utils.ts b/packages/contracts/src/utils/asset_proxy_utils.ts new file mode 100644 index 000000000..dc31c3497 --- /dev/null +++ b/packages/contracts/src/utils/asset_proxy_utils.ts @@ -0,0 +1,39 @@ +import { BigNumber } from '@0xproject/utils'; +import BN = require('bn.js'); +import ethUtil = require('ethereumjs-util'); + +import { AssetProxyId } from './types'; + +export const assetProxyUtils = { + encodeAssetProxyId(assetProxyId: AssetProxyId): Buffer { + return ethUtil.toBuffer(assetProxyId); + }, + encodeAddress(address: string): Buffer { + if (!ethUtil.isValidAddress(address)) { + throw new Error(`Invalid Address: ${address}`); + } + const encodedAddress = ethUtil.toBuffer(address); + return encodedAddress; + }, + encodeUint256(value: BigNumber): Buffer { + const formattedValue = new BN(value.toString(10)); + const encodedValue = ethUtil.toBuffer(formattedValue); + const paddedValue = ethUtil.setLengthLeft(encodedValue, 32); + return paddedValue; + }, + encodeERC20ProxyData(tokenAddress: string): string { + const encodedAssetProxyId = assetProxyUtils.encodeAssetProxyId(AssetProxyId.ERC20); + const encodedAddress = assetProxyUtils.encodeAddress(tokenAddress); + const encodedMetadata = Buffer.concat([encodedAssetProxyId, encodedAddress]); + const encodedMetadataHex = ethUtil.bufferToHex(encodedMetadata); + return encodedMetadataHex; + }, + encodeERC721ProxyData(tokenAddress: string, tokenId: BigNumber): string { + const encodedAssetProxyId = assetProxyUtils.encodeAssetProxyId(AssetProxyId.ERC721); + const encodedAddress = assetProxyUtils.encodeAddress(tokenAddress); + const encodedTokenId = assetProxyUtils.encodeUint256(tokenId); + const encodedMetadata = Buffer.concat([encodedAssetProxyId, encodedAddress, encodedTokenId]); + const encodedMetadataHex = ethUtil.bufferToHex(encodedMetadata); + return encodedMetadataHex; + }, +}; diff --git a/packages/contracts/src/utils/chai_setup.ts b/packages/contracts/src/utils/chai_setup.ts new file mode 100644 index 000000000..1a8733093 --- /dev/null +++ b/packages/contracts/src/utils/chai_setup.ts @@ -0,0 +1,13 @@ +import * as chai from 'chai'; +import chaiAsPromised = require('chai-as-promised'); +import ChaiBigNumber = require('chai-bignumber'); +import * as dirtyChai from 'dirty-chai'; + +export const chaiSetup = { + configure(): void { + chai.config.includeStack = true; + chai.use(ChaiBigNumber()); + chai.use(dirtyChai); + chai.use(chaiAsPromised); + }, +}; diff --git a/packages/contracts/src/utils/constants.ts b/packages/contracts/src/utils/constants.ts new file mode 100644 index 000000000..3fd2a0a3b --- /dev/null +++ b/packages/contracts/src/utils/constants.ts @@ -0,0 +1,42 @@ +import { ZeroEx } from '0x.js'; +import { BigNumber } from '@0xproject/utils'; +import * as ethUtil from 'ethereumjs-util'; +import * as _ from 'lodash'; + +const TESTRPC_PRIVATE_KEYS_STRINGS = [ + '0xf2f48ee19680706196e2e339e5da3491186e0c4c5030670656b0e0164837257d', + '0x5d862464fe9303452126c8bc94274b8c5f9874cbd219789b3eb2128075a76f72', + '0xdf02719c4df8b9b8ac7f551fcb5d9ef48fa27eef7a66453879f4d8fdc6e78fb1', + '0xff12e391b79415e941a94de3bf3a9aee577aed0731e297d5cfa0b8a1e02fa1d0', + '0x752dd9cf65e68cfaba7d60225cbdbc1f4729dd5e5507def72815ed0d8abc6249', + '0xefb595a0178eb79a8df953f87c5148402a224cdf725e88c0146727c6aceadccd', + '0x83c6d2cc5ddcf9711a6d59b417dc20eb48afd58d45290099e5987e3d768f328f', + '0xbb2d3f7c9583780a7d3904a2f55d792707c345f21de1bacb2d389934d82796b2', + '0xb2fd4d29c1390b71b8795ae81196bfd60293adf99f9d32a0aff06288fcdac55f', + '0x23cb7121166b9a2f93ae0b7c05bde02eae50d64449b2cbb42bc84e9d38d6cc89', +]; + +export const constants = { + INVALID_OPCODE: 'invalid opcode', + REVERT: 'revert', + TESTRPC_NETWORK_ID: 50, + MAX_ETHERTOKEN_WITHDRAW_GAS: 43000, + MAX_TOKEN_TRANSFERFROM_GAS: 80000, + MAX_TOKEN_APPROVE_GAS: 60000, + DUMMY_TOKEN_NAME: '', + DUMMY_TOKEN_SYMBOL: '', + DUMMY_TOKEN_DECIMALS: new BigNumber(18), + DUMMY_TOKEN_TOTAL_SUPPLY: new BigNumber(0), + NUM_DUMMY_ERC20_TO_DEPLOY: 3, + NUM_DUMMY_ERC721_TO_DEPLOY: 1, + NUM_ERC721_TOKENS_TO_MINT: 2, + TESTRPC_PRIVATE_KEYS: _.map(TESTRPC_PRIVATE_KEYS_STRINGS, privateKeyString => ethUtil.toBuffer(privateKeyString)), + INITIAL_ERC20_BALANCE: ZeroEx.toBaseUnitAmount(new BigNumber(10000), 18), + INITIAL_ERC20_ALLOWANCE: ZeroEx.toBaseUnitAmount(new BigNumber(10000), 18), + STATIC_ORDER_PARAMS: { + makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18), + takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18), + makerFee: ZeroEx.toBaseUnitAmount(new BigNumber(1), 18), + takerFee: ZeroEx.toBaseUnitAmount(new BigNumber(1), 18), + }, +}; diff --git a/packages/contracts/src/utils/crypto.ts b/packages/contracts/src/utils/crypto.ts new file mode 100644 index 000000000..80c5f30a5 --- /dev/null +++ b/packages/contracts/src/utils/crypto.ts @@ -0,0 +1,45 @@ +import BN = require('bn.js'); +import ABI = require('ethereumjs-abi'); +import ethUtil = require('ethereumjs-util'); +import * as _ from 'lodash'; + +export const crypto = { + /** + * We convert types from JS to Solidity as follows: + * BigNumber -> uint256 + * number -> uint8 + * string -> string + * boolean -> bool + * valid Ethereum address -> address + */ + solSHA3(args: any[]): Buffer { + return crypto._solHash(args, ABI.soliditySHA3); + }, + solSHA256(args: any[]): Buffer { + return crypto._solHash(args, ABI.soliditySHA256); + }, + _solHash(args: any[], hashFunction: (types: string[], values: any[]) => Buffer): Buffer { + const argTypes: string[] = []; + _.each(args, (arg, i) => { + const isNumber = _.isFinite(arg); + if (isNumber) { + argTypes.push('uint8'); + } else if (arg.isBigNumber) { + argTypes.push('uint256'); + args[i] = new BN(arg.toString(10), 10); + } else if (ethUtil.isValidAddress(arg)) { + argTypes.push('address'); + } else if (_.isString(arg)) { + argTypes.push('string'); + } else if (_.isBuffer(arg)) { + argTypes.push('bytes'); + } else if (_.isBoolean(arg)) { + argTypes.push('bool'); + } else { + throw new Error(`Unable to guess arg type: ${arg}`); + } + }); + const hash = hashFunction(argTypes, args); + return hash; + }, +}; diff --git a/packages/contracts/src/utils/erc20_wrapper.ts b/packages/contracts/src/utils/erc20_wrapper.ts new file mode 100644 index 000000000..f12571b48 --- /dev/null +++ b/packages/contracts/src/utils/erc20_wrapper.ts @@ -0,0 +1,116 @@ +import { Provider } from '@0xproject/types'; +import { BigNumber } from '@0xproject/utils'; +import * as _ from 'lodash'; + +import { DummyERC20TokenContract } from '../contract_wrappers/generated/dummy_e_r_c20_token'; +import { ERC20ProxyContract } from '../contract_wrappers/generated/e_r_c20_proxy'; + +import { artifacts } from './artifacts'; +import { constants } from './constants'; +import { ERC20BalancesByOwner } from './types'; +import { txDefaults } from './web3_wrapper'; + +export class ERC20Wrapper { + private _tokenOwnerAddresses: string[]; + private _contractOwnerAddress: string; + private _provider: Provider; + private _dummyTokenContracts?: DummyERC20TokenContract[]; + private _proxyContract?: ERC20ProxyContract; + constructor(provider: Provider, tokenOwnerAddresses: string[], contractOwnerAddress: string) { + this._provider = provider; + this._tokenOwnerAddresses = tokenOwnerAddresses; + this._contractOwnerAddress = contractOwnerAddress; + } + public async deployDummyTokensAsync(): Promise<DummyERC20TokenContract[]> { + this._dummyTokenContracts = await Promise.all( + _.times(constants.NUM_DUMMY_ERC20_TO_DEPLOY, () => + DummyERC20TokenContract.deployFrom0xArtifactAsync( + artifacts.DummyERC20Token, + this._provider, + txDefaults, + constants.DUMMY_TOKEN_NAME, + constants.DUMMY_TOKEN_SYMBOL, + constants.DUMMY_TOKEN_DECIMALS, + constants.DUMMY_TOKEN_TOTAL_SUPPLY, + ), + ), + ); + return this._dummyTokenContracts; + } + public async deployProxyAsync(): Promise<ERC20ProxyContract> { + this._proxyContract = await ERC20ProxyContract.deployFrom0xArtifactAsync( + artifacts.ERC20Proxy, + this._provider, + txDefaults, + ); + return this._proxyContract; + } + public async setBalancesAndAllowancesAsync() { + this._validateDummyTokenContractsExistOrThrow(); + this._validateProxyContractExistsOrThrow(); + const setBalancePromises: Array<Promise<string>> = []; + const setAllowancePromises: Array<Promise<string>> = []; + _.forEach(this._dummyTokenContracts, dummyTokenContract => { + _.forEach(this._tokenOwnerAddresses, tokenOwnerAddress => { + setBalancePromises.push( + dummyTokenContract.setBalance.sendTransactionAsync( + tokenOwnerAddress, + constants.INITIAL_ERC20_BALANCE, + { from: this._contractOwnerAddress }, + ), + ); + setAllowancePromises.push( + dummyTokenContract.approve.sendTransactionAsync( + (this._proxyContract as ERC20ProxyContract).address, + constants.INITIAL_ERC20_ALLOWANCE, + { from: tokenOwnerAddress }, + ), + ); + }); + }); + await Promise.all([...setBalancePromises, ...setAllowancePromises]); + } + public async getBalancesAsync(): Promise<ERC20BalancesByOwner> { + this._validateDummyTokenContractsExistOrThrow(); + const balancesByOwner: ERC20BalancesByOwner = {}; + const balancePromises: Array<Promise<BigNumber>> = []; + const balanceInfo: Array<{ tokenOwnerAddress: string; tokenAddress: string }> = []; + _.forEach(this._dummyTokenContracts, dummyTokenContract => { + _.forEach(this._tokenOwnerAddresses, tokenOwnerAddress => { + balancePromises.push(dummyTokenContract.balanceOf.callAsync(tokenOwnerAddress)); + balanceInfo.push({ + tokenOwnerAddress, + tokenAddress: dummyTokenContract.address, + }); + }); + }); + const balances = await Promise.all(balancePromises); + _.forEach(balances, (balance, balanceIndex) => { + const tokenAddress = balanceInfo[balanceIndex].tokenAddress; + const tokenOwnerAddress = balanceInfo[balanceIndex].tokenOwnerAddress; + if (_.isUndefined(balancesByOwner[tokenOwnerAddress])) { + balancesByOwner[tokenOwnerAddress] = {}; + } + const wrappedBalance = new BigNumber(balance); + balancesByOwner[tokenOwnerAddress][tokenAddress] = wrappedBalance; + }); + return balancesByOwner; + } + public getTokenOwnerAddresses(): string[] { + return this._tokenOwnerAddresses; + } + public getTokenAddresses(): string[] { + const tokenAddresses = _.map(this._dummyTokenContracts, dummyTokenContract => dummyTokenContract.address); + return tokenAddresses; + } + private _validateDummyTokenContractsExistOrThrow() { + if (_.isUndefined(this._dummyTokenContracts)) { + throw new Error('Dummy ERC20 tokens not yet deployed, please call "deployDummyTokensAsync"'); + } + } + private _validateProxyContractExistsOrThrow() { + if (_.isUndefined(this._proxyContract)) { + throw new Error('ERC20 proxy contract not yet deployed, please call "deployProxyAsync"'); + } + } +} diff --git a/packages/contracts/src/utils/erc721_wrapper.ts b/packages/contracts/src/utils/erc721_wrapper.ts new file mode 100644 index 000000000..5b5de262f --- /dev/null +++ b/packages/contracts/src/utils/erc721_wrapper.ts @@ -0,0 +1,145 @@ +import { ZeroEx } from '0x.js'; +import { Provider } from '@0xproject/types'; +import { BigNumber } from '@0xproject/utils'; +import * as _ from 'lodash'; + +import { DummyERC721TokenContract } from '../contract_wrappers/generated/dummy_e_r_c721_token'; +import { ERC721ProxyContract } from '../contract_wrappers/generated/e_r_c721_proxy'; + +import { artifacts } from './artifacts'; +import { constants } from './constants'; +import { ERC721TokenIdsByOwner } from './types'; +import { txDefaults } from './web3_wrapper'; + +export class ERC721Wrapper { + private _tokenOwnerAddresses: string[]; + private _contractOwnerAddress: string; + private _provider: Provider; + private _dummyTokenContracts?: DummyERC721TokenContract[]; + private _proxyContract?: ERC721ProxyContract; + private _initialTokenIdsByOwner: ERC721TokenIdsByOwner = {}; + constructor(provider: Provider, tokenOwnerAddresses: string[], contractOwnerAddress: string) { + this._provider = provider; + this._tokenOwnerAddresses = tokenOwnerAddresses; + this._contractOwnerAddress = contractOwnerAddress; + } + public async deployDummyTokensAsync(): Promise<DummyERC721TokenContract[]> { + this._dummyTokenContracts = await Promise.all( + _.times(constants.NUM_DUMMY_ERC721_TO_DEPLOY, () => + DummyERC721TokenContract.deployFrom0xArtifactAsync( + artifacts.DummyERC721Token, + this._provider, + txDefaults, + constants.DUMMY_TOKEN_NAME, + constants.DUMMY_TOKEN_SYMBOL, + ), + ), + ); + return this._dummyTokenContracts; + } + public async deployProxyAsync(): Promise<ERC721ProxyContract> { + this._proxyContract = await ERC721ProxyContract.deployFrom0xArtifactAsync( + artifacts.ERC721Proxy, + this._provider, + txDefaults, + ); + return this._proxyContract; + } + public async setBalancesAndAllowancesAsync() { + this._validateDummyTokenContractsExistOrThrow(); + this._validateProxyContractExistsOrThrow(); + const setBalancePromises: Array<Promise<string>> = []; + const setAllowancePromises: Array<Promise<string>> = []; + this._initialTokenIdsByOwner = {}; + _.forEach(this._dummyTokenContracts, dummyTokenContract => { + _.forEach(this._tokenOwnerAddresses, tokenOwnerAddress => { + _.forEach(_.range(constants.NUM_ERC721_TOKENS_TO_MINT), () => { + const tokenId = ZeroEx.generatePseudoRandomSalt(); + setBalancePromises.push( + dummyTokenContract.mint.sendTransactionAsync(tokenOwnerAddress, tokenId, { + from: this._contractOwnerAddress, + }), + ); + if (_.isUndefined(this._initialTokenIdsByOwner[tokenOwnerAddress])) { + this._initialTokenIdsByOwner[tokenOwnerAddress] = { + [dummyTokenContract.address]: [], + }; + } + if (_.isUndefined(this._initialTokenIdsByOwner[tokenOwnerAddress][dummyTokenContract.address])) { + this._initialTokenIdsByOwner[tokenOwnerAddress][dummyTokenContract.address] = []; + } + this._initialTokenIdsByOwner[tokenOwnerAddress][dummyTokenContract.address].push(tokenId); + }); + const shouldApprove = true; + setAllowancePromises.push( + dummyTokenContract.setApprovalForAll.sendTransactionAsync( + (this._proxyContract as ERC721ProxyContract).address, + shouldApprove, + { from: tokenOwnerAddress }, + ), + ); + }); + }); + await Promise.all([...setBalancePromises, ...setAllowancePromises]); + } + public async getBalancesAsync(): Promise<ERC721TokenIdsByOwner> { + this._validateDummyTokenContractsExistOrThrow(); + this._validateBalancesAndAllowancesSetOrThrow(); + const tokenIdsByOwner: ERC721TokenIdsByOwner = {}; + const tokenOwnerPromises: Array<Promise<string>> = []; + const tokenInfo: Array<{ tokenId: BigNumber; tokenAddress: string }> = []; + _.forEach(this._dummyTokenContracts, dummyTokenContract => { + _.forEach(this._tokenOwnerAddresses, tokenOwnerAddress => { + const initialTokenOwnerIds = this._initialTokenIdsByOwner[tokenOwnerAddress][ + dummyTokenContract.address + ]; + _.forEach(initialTokenOwnerIds, tokenId => { + tokenOwnerPromises.push(dummyTokenContract.ownerOf.callAsync(tokenId)); + tokenInfo.push({ + tokenId, + tokenAddress: dummyTokenContract.address, + }); + }); + }); + }); + const tokenOwnerAddresses = await Promise.all(tokenOwnerPromises); + _.forEach(tokenOwnerAddresses, (tokenOwnerAddress, ownerIndex) => { + const tokenAddress = tokenInfo[ownerIndex].tokenAddress; + const tokenId = tokenInfo[ownerIndex].tokenId; + if (_.isUndefined(tokenIdsByOwner[tokenOwnerAddress])) { + tokenIdsByOwner[tokenOwnerAddress] = { + [tokenAddress]: [], + }; + } + if (_.isUndefined(tokenIdsByOwner[tokenOwnerAddress][tokenAddress])) { + tokenIdsByOwner[tokenOwnerAddress][tokenAddress] = []; + } + tokenIdsByOwner[tokenOwnerAddress][tokenAddress].push(tokenId); + }); + return tokenIdsByOwner; + } + public getTokenOwnerAddresses(): string[] { + return this._tokenOwnerAddresses; + } + public getTokenAddresses(): string[] { + const tokenAddresses = _.map(this._dummyTokenContracts, dummyTokenContract => dummyTokenContract.address); + return tokenAddresses; + } + private _validateDummyTokenContractsExistOrThrow() { + if (_.isUndefined(this._dummyTokenContracts)) { + throw new Error('Dummy ERC721 tokens not yet deployed, please call "deployDummyTokensAsync"'); + } + } + private _validateProxyContractExistsOrThrow() { + if (_.isUndefined(this._proxyContract)) { + throw new Error('ERC721 proxy contract not yet deployed, please call "deployProxyAsync"'); + } + } + private _validateBalancesAndAllowancesSetOrThrow() { + if (_.keys(this._initialTokenIdsByOwner).length === 0) { + throw new Error( + 'Dummy ERC721 balances and allowances not yet set, please call "setBalancesAndAllowancesAsync"', + ); + } + } +} diff --git a/packages/contracts/src/utils/exchange_wrapper.ts b/packages/contracts/src/utils/exchange_wrapper.ts new file mode 100644 index 000000000..27fdd698f --- /dev/null +++ b/packages/contracts/src/utils/exchange_wrapper.ts @@ -0,0 +1,234 @@ +import { TransactionReceiptWithDecodedLogs, ZeroEx } from '0x.js'; +import { BigNumber } from '@0xproject/utils'; +import * as _ from 'lodash'; +import * as Web3 from 'web3'; + +import { ExchangeContract } from '../contract_wrappers/generated/exchange'; + +import { constants } from './constants'; +import { formatters } from './formatters'; +import { LogDecoder } from './log_decoder'; +import { orderUtils } from './order_utils'; +import { AssetProxyId, SignedOrder, SignedTransaction } from './types'; + +export class ExchangeWrapper { + private _exchange: ExchangeContract; + private _logDecoder: LogDecoder = new LogDecoder(constants.TESTRPC_NETWORK_ID); + private _zeroEx: ZeroEx; + constructor(exchangeContract: ExchangeContract, zeroEx: ZeroEx) { + this._exchange = exchangeContract; + this._zeroEx = zeroEx; + } + public async fillOrderAsync( + signedOrder: SignedOrder, + from: string, + opts: { takerAssetFillAmount?: BigNumber } = {}, + ): Promise<TransactionReceiptWithDecodedLogs> { + const params = orderUtils.createFill(signedOrder, opts.takerAssetFillAmount); + const txHash = await this._exchange.fillOrder.sendTransactionAsync( + params.order, + params.takerAssetFillAmount, + params.signature, + { from }, + ); + const tx = await this._getTxWithDecodedExchangeLogsAsync(txHash); + return tx; + } + public async cancelOrderAsync(signedOrder: SignedOrder, from: string): Promise<TransactionReceiptWithDecodedLogs> { + const params = orderUtils.createCancel(signedOrder); + const txHash = await this._exchange.cancelOrder.sendTransactionAsync(params.order, { from }); + const tx = await this._getTxWithDecodedExchangeLogsAsync(txHash); + return tx; + } + public async fillOrKillOrderAsync( + signedOrder: SignedOrder, + from: string, + opts: { takerAssetFillAmount?: BigNumber } = {}, + ): Promise<TransactionReceiptWithDecodedLogs> { + const params = orderUtils.createFill(signedOrder, opts.takerAssetFillAmount); + const txHash = await this._exchange.fillOrKillOrder.sendTransactionAsync( + params.order, + params.takerAssetFillAmount, + params.signature, + { from }, + ); + const tx = await this._getTxWithDecodedExchangeLogsAsync(txHash); + return tx; + } + public async fillOrderNoThrowAsync( + signedOrder: SignedOrder, + from: string, + opts: { takerAssetFillAmount?: BigNumber } = {}, + ): Promise<TransactionReceiptWithDecodedLogs> { + const params = orderUtils.createFill(signedOrder, opts.takerAssetFillAmount); + const txHash = await this._exchange.fillOrderNoThrow.sendTransactionAsync( + params.order, + params.takerAssetFillAmount, + params.signature, + { from }, + ); + const tx = await this._getTxWithDecodedExchangeLogsAsync(txHash); + return tx; + } + public async batchFillOrdersAsync( + orders: SignedOrder[], + from: string, + opts: { takerAssetFillAmounts?: BigNumber[] } = {}, + ): Promise<TransactionReceiptWithDecodedLogs> { + const params = formatters.createBatchFill(orders, opts.takerAssetFillAmounts); + const txHash = await this._exchange.batchFillOrders.sendTransactionAsync( + params.orders, + params.takerAssetFillAmounts, + params.signatures, + { from }, + ); + const tx = await this._getTxWithDecodedExchangeLogsAsync(txHash); + return tx; + } + public async batchFillOrKillOrdersAsync( + orders: SignedOrder[], + from: string, + opts: { takerAssetFillAmounts?: BigNumber[] } = {}, + ): Promise<TransactionReceiptWithDecodedLogs> { + const params = formatters.createBatchFill(orders, opts.takerAssetFillAmounts); + const txHash = await this._exchange.batchFillOrKillOrders.sendTransactionAsync( + params.orders, + params.takerAssetFillAmounts, + params.signatures, + { from }, + ); + const tx = await this._getTxWithDecodedExchangeLogsAsync(txHash); + return tx; + } + public async batchFillOrdersNoThrowAsync( + orders: SignedOrder[], + from: string, + opts: { takerAssetFillAmounts?: BigNumber[] } = {}, + ): Promise<TransactionReceiptWithDecodedLogs> { + const params = formatters.createBatchFill(orders, opts.takerAssetFillAmounts); + const txHash = await this._exchange.batchFillOrdersNoThrow.sendTransactionAsync( + params.orders, + params.takerAssetFillAmounts, + params.signatures, + { from }, + ); + const tx = await this._getTxWithDecodedExchangeLogsAsync(txHash); + return tx; + } + public async marketSellOrdersAsync( + orders: SignedOrder[], + from: string, + opts: { takerAssetFillAmount: BigNumber }, + ): Promise<TransactionReceiptWithDecodedLogs> { + const params = formatters.createMarketSellOrders(orders, opts.takerAssetFillAmount); + const txHash = await this._exchange.marketSellOrders.sendTransactionAsync( + params.orders, + params.takerAssetFillAmount, + params.signatures, + { from }, + ); + const tx = await this._getTxWithDecodedExchangeLogsAsync(txHash); + return tx; + } + public async marketSellOrdersNoThrowAsync( + orders: SignedOrder[], + from: string, + opts: { takerAssetFillAmount: BigNumber }, + ): Promise<TransactionReceiptWithDecodedLogs> { + const params = formatters.createMarketSellOrders(orders, opts.takerAssetFillAmount); + const txHash = await this._exchange.marketSellOrdersNoThrow.sendTransactionAsync( + params.orders, + params.takerAssetFillAmount, + params.signatures, + { from }, + ); + const tx = await this._getTxWithDecodedExchangeLogsAsync(txHash); + return tx; + } + public async marketBuyOrdersAsync( + orders: SignedOrder[], + from: string, + opts: { makerAssetFillAmount: BigNumber }, + ): Promise<TransactionReceiptWithDecodedLogs> { + const params = formatters.createMarketBuyOrders(orders, opts.makerAssetFillAmount); + const txHash = await this._exchange.marketBuyOrders.sendTransactionAsync( + params.orders, + params.makerAssetFillAmount, + params.signatures, + { from }, + ); + const tx = await this._getTxWithDecodedExchangeLogsAsync(txHash); + return tx; + } + public async marketBuyOrdersNoThrowAsync( + orders: SignedOrder[], + from: string, + opts: { makerAssetFillAmount: BigNumber }, + ): Promise<TransactionReceiptWithDecodedLogs> { + const params = formatters.createMarketBuyOrders(orders, opts.makerAssetFillAmount); + const txHash = await this._exchange.marketBuyOrdersNoThrow.sendTransactionAsync( + params.orders, + params.makerAssetFillAmount, + params.signatures, + { from }, + ); + const tx = await this._getTxWithDecodedExchangeLogsAsync(txHash); + return tx; + } + public async batchCancelOrdersAsync( + orders: SignedOrder[], + from: string, + ): Promise<TransactionReceiptWithDecodedLogs> { + const params = formatters.createBatchCancel(orders); + const txHash = await this._exchange.batchCancelOrders.sendTransactionAsync(params.orders, { from }); + const tx = await this._getTxWithDecodedExchangeLogsAsync(txHash); + return tx; + } + public async cancelOrdersUpToAsync(salt: BigNumber, from: string): Promise<TransactionReceiptWithDecodedLogs> { + const txHash = await this._exchange.cancelOrdersUpTo.sendTransactionAsync(salt, { from }); + const tx = await this._getTxWithDecodedExchangeLogsAsync(txHash); + return tx; + } + public async registerAssetProxyAsync( + assetProxyId: AssetProxyId, + assetProxyAddress: string, + from: string, + opts: { oldAssetProxyAddressIfExists?: string } = {}, + ): Promise<TransactionReceiptWithDecodedLogs> { + const oldAssetProxyAddress = _.isUndefined(opts.oldAssetProxyAddressIfExists) + ? ZeroEx.NULL_ADDRESS + : opts.oldAssetProxyAddressIfExists; + const txHash = await this._exchange.registerAssetProxy.sendTransactionAsync( + assetProxyId, + assetProxyAddress, + oldAssetProxyAddress, + { from }, + ); + const tx = await this._getTxWithDecodedExchangeLogsAsync(txHash); + return tx; + } + public async executeTransactionAsync( + signedTx: SignedTransaction, + from: string, + ): Promise<TransactionReceiptWithDecodedLogs> { + const txHash = await this._exchange.executeTransaction.sendTransactionAsync( + signedTx.salt, + signedTx.signer, + signedTx.data, + signedTx.signature, + { from }, + ); + const tx = await this._getTxWithDecodedExchangeLogsAsync(txHash); + return tx; + } + public async getTakerAssetFilledAmountAsync(orderHashHex: string): Promise<BigNumber> { + const filledAmount = new BigNumber(await this._exchange.filled.callAsync(orderHashHex)); + return filledAmount; + } + private async _getTxWithDecodedExchangeLogsAsync(txHash: string) { + const tx = await this._zeroEx.awaitTransactionMinedAsync(txHash); + tx.logs = _.filter(tx.logs, log => log.address === this._exchange.address); + tx.logs = _.map(tx.logs, log => this._logDecoder.decodeLogOrThrow(log)); + return tx; + } +} diff --git a/packages/contracts/src/utils/formatters.ts b/packages/contracts/src/utils/formatters.ts new file mode 100644 index 000000000..e706c15b5 --- /dev/null +++ b/packages/contracts/src/utils/formatters.ts @@ -0,0 +1,60 @@ +import { BigNumber } from '@0xproject/utils'; +import * as _ from 'lodash'; + +import { orderUtils } from './order_utils'; +import { BatchCancelOrders, BatchFillOrders, MarketBuyOrders, MarketSellOrders, SignedOrder } from './types'; + +export const formatters = { + createBatchFill(signedOrders: SignedOrder[], takerAssetFillAmounts: BigNumber[] = []) { + const batchFill: BatchFillOrders = { + orders: [], + signatures: [], + takerAssetFillAmounts, + }; + _.forEach(signedOrders, signedOrder => { + const orderStruct = orderUtils.getOrderStruct(signedOrder); + batchFill.orders.push(orderStruct); + batchFill.signatures.push(signedOrder.signature); + if (takerAssetFillAmounts.length < signedOrders.length) { + batchFill.takerAssetFillAmounts.push(signedOrder.takerAssetAmount); + } + }); + return batchFill; + }, + createMarketSellOrders(signedOrders: SignedOrder[], takerAssetFillAmount: BigNumber) { + const marketSellOrders: MarketSellOrders = { + orders: [], + signatures: [], + takerAssetFillAmount, + }; + _.forEach(signedOrders, signedOrder => { + const orderStruct = orderUtils.getOrderStruct(signedOrder); + marketSellOrders.orders.push(orderStruct); + marketSellOrders.signatures.push(signedOrder.signature); + }); + return marketSellOrders; + }, + createMarketBuyOrders(signedOrders: SignedOrder[], makerAssetFillAmount: BigNumber) { + const marketBuyOrders: MarketBuyOrders = { + orders: [], + signatures: [], + makerAssetFillAmount, + }; + _.forEach(signedOrders, signedOrder => { + const orderStruct = orderUtils.getOrderStruct(signedOrder); + marketBuyOrders.orders.push(orderStruct); + marketBuyOrders.signatures.push(signedOrder.signature); + }); + return marketBuyOrders; + }, + createBatchCancel(signedOrders: SignedOrder[]) { + const batchCancel: BatchCancelOrders = { + orders: [], + }; + _.forEach(signedOrders, signedOrder => { + const orderStruct = orderUtils.getOrderStruct(signedOrder); + batchCancel.orders.push(orderStruct); + }); + return batchCancel; + }, +}; diff --git a/packages/contracts/src/utils/log_decoder.ts b/packages/contracts/src/utils/log_decoder.ts new file mode 100644 index 000000000..747c7644d --- /dev/null +++ b/packages/contracts/src/utils/log_decoder.ts @@ -0,0 +1,39 @@ +import { ContractArtifact } from '@0xproject/sol-compiler'; +import { AbiDefinition, LogEntry, LogWithDecodedArgs, RawLog } from '@0xproject/types'; +import { AbiDecoder, BigNumber } from '@0xproject/utils'; +import * as _ from 'lodash'; + +import { artifacts } from './artifacts'; + +export class LogDecoder { + private _abiDecoder: AbiDecoder; + constructor(networkIdIfExists?: number) { + if (_.isUndefined(networkIdIfExists)) { + throw new Error('networkId not specified'); + } + const abiArrays: AbiDefinition[][] = []; + _.forEach(artifacts, (artifact: ContractArtifact) => { + const compilerOutput = artifact.compilerOutput; + abiArrays.push(compilerOutput.abi); + }); + this._abiDecoder = new AbiDecoder(abiArrays); + } + public decodeLogOrThrow<ArgsType>(log: LogEntry): LogWithDecodedArgs<ArgsType> | RawLog { + const logWithDecodedArgsOrLog = this._abiDecoder.tryToDecodeLogOrNoop(log); + if (_.isUndefined((logWithDecodedArgsOrLog as LogWithDecodedArgs<ArgsType>).args)) { + throw new Error(`Unable to decode log: ${JSON.stringify(log)}`); + } + wrapLogBigNumbers(logWithDecodedArgsOrLog); + return logWithDecodedArgsOrLog; + } +} + +function wrapLogBigNumbers(log: any): any { + const argNames = _.keys(log.args); + for (const argName of argNames) { + const isWeb3BigNumber = _.startsWith(log.args[argName].constructor.toString(), 'function BigNumber('); + if (isWeb3BigNumber) { + log.args[argName] = new BigNumber(log.args[argName]); + } + } +} diff --git a/packages/contracts/src/utils/multi_sig_wrapper.ts b/packages/contracts/src/utils/multi_sig_wrapper.ts new file mode 100644 index 000000000..41a1dd8d9 --- /dev/null +++ b/packages/contracts/src/utils/multi_sig_wrapper.ts @@ -0,0 +1,43 @@ +import { AbiDefinition, MethodAbi } from '@0xproject/types'; +import { BigNumber } from '@0xproject/utils'; +import ABI = require('ethereumjs-abi'); +import ethUtil = require('ethereumjs-util'); +import * as _ from 'lodash'; +import * as Web3 from 'web3'; + +import { MultiSigWalletContract } from '../contract_wrappers/generated/multi_sig_wallet'; + +import { TransactionDataParams } from './types'; + +export class MultiSigWrapper { + private _multiSig: MultiSigWalletContract; + public static encodeFnArgs(name: string, abi: AbiDefinition[], args: any[]): string { + const abiEntity = _.find(abi, { name }) as MethodAbi; + if (_.isUndefined(abiEntity)) { + throw new Error(`Did not find abi entry for name: ${name}`); + } + const types = _.map(abiEntity.inputs, input => input.type); + const funcSig = ethUtil.bufferToHex(ABI.methodID(name, types)); + const argsData = _.map(args, arg => { + const target = _.isBoolean(arg) ? +arg : arg; + const targetBuff = ethUtil.toBuffer(target); + return ethUtil.setLengthLeft(targetBuff, 32).toString('hex'); + }); + return funcSig + argsData.join(''); + } + constructor(multiSigContract: MultiSigWalletContract) { + this._multiSig = multiSigContract; + } + public async submitTransactionAsync( + destination: string, + from: string, + dataParams: TransactionDataParams, + value: BigNumber = new BigNumber(0), + ): Promise<string> { + const { name, abi, args = [] } = dataParams; + const encoded = MultiSigWrapper.encodeFnArgs(name, abi, args); + return this._multiSig.submitTransaction.sendTransactionAsync(destination, value, encoded, { + from, + }); + } +} diff --git a/packages/contracts/src/utils/order_factory.ts b/packages/contracts/src/utils/order_factory.ts new file mode 100644 index 000000000..044e9b865 --- /dev/null +++ b/packages/contracts/src/utils/order_factory.ts @@ -0,0 +1,37 @@ +import { ZeroEx } from '0x.js'; +import { BigNumber } from '@0xproject/utils'; +import * as _ from 'lodash'; + +import { orderUtils } from './order_utils'; +import { signingUtils } from './signing_utils'; +import { SignatureType, SignedOrder, UnsignedOrder } from './types'; + +export class OrderFactory { + private _defaultOrderParams: Partial<UnsignedOrder>; + private _privateKey: Buffer; + constructor(privateKey: Buffer, defaultOrderParams: Partial<UnsignedOrder>) { + this._defaultOrderParams = defaultOrderParams; + this._privateKey = privateKey; + } + public newSignedOrder( + customOrderParams: Partial<UnsignedOrder> = {}, + signatureType: SignatureType = SignatureType.Ecrecover, + ): SignedOrder { + const randomExpiration = new BigNumber(Math.floor((Date.now() + Math.random() * 100000000000) / 1000)); + const order = ({ + senderAddress: ZeroEx.NULL_ADDRESS, + expirationTimeSeconds: randomExpiration, + salt: ZeroEx.generatePseudoRandomSalt(), + takerAddress: ZeroEx.NULL_ADDRESS, + ...this._defaultOrderParams, + ...customOrderParams, + } as any) as UnsignedOrder; + const orderHashBuff = orderUtils.getOrderHashBuff(order); + const signature = signingUtils.signMessage(orderHashBuff, this._privateKey, signatureType); + const signedOrder = { + ...order, + signature: `0x${signature.toString('hex')}`, + }; + return signedOrder; + } +} diff --git a/packages/contracts/src/utils/order_utils.ts b/packages/contracts/src/utils/order_utils.ts new file mode 100644 index 000000000..10bbf4f7c --- /dev/null +++ b/packages/contracts/src/utils/order_utils.ts @@ -0,0 +1,83 @@ +import { BigNumber } from '@0xproject/utils'; +import { Web3Wrapper } from '@0xproject/web3-wrapper'; +import ethUtil = require('ethereumjs-util'); +import * as _ from 'lodash'; + +import { crypto } from './crypto'; +import { OrderStruct, SignatureType, SignedOrder, UnsignedOrder } from './types'; + +export const orderUtils = { + createFill: (signedOrder: SignedOrder, takerAssetFillAmount?: BigNumber) => { + const fill = { + order: orderUtils.getOrderStruct(signedOrder), + takerAssetFillAmount: takerAssetFillAmount || signedOrder.takerAssetAmount, + signature: signedOrder.signature, + }; + return fill; + }, + createCancel(signedOrder: SignedOrder, takerAssetCancelAmount?: BigNumber) { + const cancel = { + order: orderUtils.getOrderStruct(signedOrder), + takerAssetCancelAmount: takerAssetCancelAmount || signedOrder.takerAssetAmount, + }; + return cancel; + }, + getOrderStruct(signedOrder: SignedOrder): OrderStruct { + const orderStruct = { + senderAddress: signedOrder.senderAddress, + makerAddress: signedOrder.makerAddress, + takerAddress: signedOrder.takerAddress, + feeRecipientAddress: signedOrder.feeRecipientAddress, + makerAssetAmount: signedOrder.makerAssetAmount, + takerAssetAmount: signedOrder.takerAssetAmount, + makerFee: signedOrder.makerFee, + takerFee: signedOrder.takerFee, + expirationTimeSeconds: signedOrder.expirationTimeSeconds, + salt: signedOrder.salt, + makerAssetData: signedOrder.makerAssetData, + takerAssetData: signedOrder.takerAssetData, + }; + return orderStruct; + }, + getOrderHashBuff(order: SignedOrder | UnsignedOrder): Buffer { + const orderSchemaHashBuff = crypto.solSHA3([ + 'address exchangeAddress', + 'address makerAddress', + 'address takerAddress', + 'address feeRecipientAddress', + 'address senderAddress', + 'uint256 makerAssetAmount', + 'uint256 takerAssetAmount', + 'uint256 makerFee', + 'uint256 takerFee', + 'uint256 expirationTimeSeconds', + 'uint256 salt', + 'bytes makerAssetData', + 'bytes takerAssetData', + ]); + const orderParamsHashBuff = crypto.solSHA3([ + order.exchangeAddress, + order.makerAddress, + order.takerAddress, + order.feeRecipientAddress, + order.senderAddress, + order.makerAssetAmount, + order.takerAssetAmount, + order.makerFee, + order.takerFee, + order.expirationTimeSeconds, + order.salt, + ethUtil.toBuffer(order.makerAssetData), + ethUtil.toBuffer(order.takerAssetData), + ]); + const orderSchemaHashHex = `0x${orderSchemaHashBuff.toString('hex')}`; + const orderParamsHashHex = `0x${orderParamsHashBuff.toString('hex')}`; + const orderHashBuff = crypto.solSHA3([new BigNumber(orderSchemaHashHex), new BigNumber(orderParamsHashHex)]); + return orderHashBuff; + }, + getOrderHashHex(order: SignedOrder | UnsignedOrder): string { + const orderHashBuff = orderUtils.getOrderHashBuff(order); + const orderHashHex = `0x${orderHashBuff.toString('hex')}`; + return orderHashHex; + }, +}; diff --git a/packages/contracts/src/utils/signing_utils.ts b/packages/contracts/src/utils/signing_utils.ts new file mode 100644 index 000000000..61ab1f138 --- /dev/null +++ b/packages/contracts/src/utils/signing_utils.ts @@ -0,0 +1,30 @@ +import * as ethUtil from 'ethereumjs-util'; + +import { SignatureType } from './types'; + +export const signingUtils = { + signMessage(message: Buffer, privateKey: Buffer, signatureType: SignatureType): Buffer { + if (signatureType === SignatureType.Ecrecover) { + const prefixedMessage = ethUtil.hashPersonalMessage(message); + const ecSignature = ethUtil.ecsign(prefixedMessage, privateKey); + const signature = Buffer.concat([ + ethUtil.toBuffer(signatureType), + ethUtil.toBuffer(ecSignature.v), + ecSignature.r, + ecSignature.s, + ]); + return signature; + } else if (signatureType === SignatureType.EIP712) { + const ecSignature = ethUtil.ecsign(message, privateKey); + const signature = Buffer.concat([ + ethUtil.toBuffer(signatureType), + ethUtil.toBuffer(ecSignature.v), + ecSignature.r, + ecSignature.s, + ]); + return signature; + } else { + throw new Error(`${signatureType} is not a valid signature type`); + } + }, +}; diff --git a/packages/contracts/src/utils/token_registry_wrapper.ts b/packages/contracts/src/utils/token_registry_wrapper.ts new file mode 100644 index 000000000..86daeca62 --- /dev/null +++ b/packages/contracts/src/utils/token_registry_wrapper.ts @@ -0,0 +1,60 @@ +import * as Web3 from 'web3'; + +import { TokenRegistryContract } from '../contract_wrappers/generated/token_registry'; + +import { Token } from './types'; + +export class TokenRegWrapper { + private _tokenReg: TokenRegistryContract; + constructor(tokenRegContract: TokenRegistryContract) { + this._tokenReg = tokenRegContract; + } + public async addTokenAsync(token: Token, from: string): Promise<string> { + const tx = this._tokenReg.addToken.sendTransactionAsync( + token.address as string, + token.name, + token.symbol, + token.decimals, + token.ipfsHash, + token.swarmHash, + { from }, + ); + return tx; + } + public async getTokenMetaDataAsync(tokenAddress: string): Promise<Token> { + const data = await this._tokenReg.getTokenMetaData.callAsync(tokenAddress); + const token: Token = { + address: data[0], + name: data[1], + symbol: data[2], + decimals: data[3], + ipfsHash: data[4], + swarmHash: data[5], + }; + return token; + } + public async getTokenByNameAsync(tokenName: string): Promise<Token> { + const data = await this._tokenReg.getTokenByName.callAsync(tokenName); + const token: Token = { + address: data[0], + name: data[1], + symbol: data[2], + decimals: data[3], + ipfsHash: data[4], + swarmHash: data[5], + }; + return token; + } + public async getTokenBySymbolAsync(tokenSymbol: string): Promise<Token> { + const data = await this._tokenReg.getTokenBySymbol.callAsync(tokenSymbol); + const token: Token = { + address: data[0], + name: data[1], + symbol: data[2], + decimals: data[3], + ipfsHash: data[4], + swarmHash: data[5], + }; + return token; + } +} diff --git a/packages/contracts/src/utils/transaction_factory.ts b/packages/contracts/src/utils/transaction_factory.ts new file mode 100644 index 000000000..3a4f48330 --- /dev/null +++ b/packages/contracts/src/utils/transaction_factory.ts @@ -0,0 +1,35 @@ +import { ZeroEx } from '0x.js'; +import { BigNumber } from '@0xproject/utils'; +import * as ethUtil from 'ethereumjs-util'; + +import { crypto } from './crypto'; +import { signingUtils } from './signing_utils'; +import { SignatureType, SignedTransaction } from './types'; + +export class TransactionFactory { + private _signer: string; + private _exchangeAddress: string; + private _privateKey: Buffer; + constructor(privateKey: Buffer, exchangeAddress: string) { + this._privateKey = privateKey; + this._exchangeAddress = exchangeAddress; + const signerBuff = ethUtil.privateToAddress(this._privateKey); + this._signer = `0x${signerBuff.toString('hex')}`; + } + public newSignedTransaction( + data: string, + signatureType: SignatureType = SignatureType.Ecrecover, + ): SignedTransaction { + const salt = ZeroEx.generatePseudoRandomSalt(); + const txHash = crypto.solSHA3([this._exchangeAddress, salt, ethUtil.toBuffer(data)]); + const signature = signingUtils.signMessage(txHash, this._privateKey, signatureType); + const signedTx = { + exchangeAddress: this._exchangeAddress, + salt, + signer: this._signer, + data, + signature: `0x${signature.toString('hex')}`, + }; + return signedTx; + } +} diff --git a/packages/contracts/src/utils/types.ts b/packages/contracts/src/utils/types.ts new file mode 100644 index 000000000..234b14ef9 --- /dev/null +++ b/packages/contracts/src/utils/types.ts @@ -0,0 +1,145 @@ +import { AbiDefinition, ContractAbi } from '@0xproject/types'; +import { BigNumber } from '@0xproject/utils'; + +export interface ERC20BalancesByOwner { + [ownerAddress: string]: { + [tokenAddress: string]: BigNumber; + }; +} + +export interface ERC721TokenIdsByOwner { + [ownerAddress: string]: { + [tokenAddress: string]: BigNumber[]; + }; +} + +export interface SubmissionContractEventArgs { + transactionId: BigNumber; +} + +export interface BatchFillOrders { + orders: OrderStruct[]; + signatures: string[]; + takerAssetFillAmounts: BigNumber[]; +} + +export interface MarketSellOrders { + orders: OrderStruct[]; + signatures: string[]; + takerAssetFillAmount: BigNumber; +} + +export interface MarketBuyOrders { + orders: OrderStruct[]; + signatures: string[]; + makerAssetFillAmount: BigNumber; +} + +export interface BatchCancelOrders { + orders: OrderStruct[]; +} + +export interface CancelOrdersBefore { + salt: BigNumber; +} + +export enum AssetProxyId { + INVALID, + ERC20, + ERC721, +} + +export interface TransactionDataParams { + name: string; + abi: AbiDefinition[]; + args: any[]; +} + +export interface MultiSigConfig { + owners: string[]; + confirmationsRequired: number; + secondsRequired: number; +} + +export interface MultiSigConfigByNetwork { + [networkName: string]: MultiSigConfig; +} + +export interface Token { + address?: string; + name: string; + symbol: string; + decimals: number; + ipfsHash: string; + swarmHash: string; +} + +export enum ExchangeContractErrs { + ERROR_ORDER_EXPIRED, + ERROR_ORDER_FULLY_FILLED, + ERROR_ORDER_CANCELLED, + ERROR_ROUNDING_ERROR_TOO_LARGE, + ERROR_INSUFFICIENT_BALANCE_OR_ALLOWANCE, +} + +export enum ContractName { + TokenRegistry = 'TokenRegistry', + MultiSigWalletWithTimeLock = 'MultiSigWalletWithTimeLock', + Exchange = 'Exchange', + ZRXToken = 'ZRXToken', + DummyERC20Token = 'DummyERC20Token', + EtherToken = 'WETH9', + MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress = 'MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress', + AccountLevels = 'AccountLevels', + EtherDelta = 'EtherDelta', + Arbitrage = 'Arbitrage', + TestAssetProxyDispatcher = 'TestAssetProxyDispatcher', + TestLibs = 'TestLibs', + TestSignatureValidator = 'TestSignatureValidator', + ERC20Proxy = 'ERC20Proxy', + ERC721Proxy = 'ERC721Proxy', + DummyERC721Token = 'DummyERC721Token', + TestLibBytes = 'TestLibBytes', + Authorizable = 'Authorizable', +} + +export interface SignedOrder extends UnsignedOrder { + signature: string; +} + +export interface OrderStruct { + senderAddress: string; + makerAddress: string; + takerAddress: string; + feeRecipientAddress: string; + makerAssetAmount: BigNumber; + takerAssetAmount: BigNumber; + makerFee: BigNumber; + takerFee: BigNumber; + expirationTimeSeconds: BigNumber; + salt: BigNumber; + makerAssetData: string; + takerAssetData: string; +} + +export interface UnsignedOrder extends OrderStruct { + exchangeAddress: string; +} + +export enum SignatureType { + Illegal, + Invalid, + Caller, + Ecrecover, + EIP712, + Trezor, + Contract, +} + +export interface SignedTransaction { + exchangeAddress: string; + salt: BigNumber; + signer: string; + data: string; + signature: string; +} diff --git a/packages/contracts/src/utils/web3_wrapper.ts b/packages/contracts/src/utils/web3_wrapper.ts new file mode 100644 index 000000000..ed1c488a2 --- /dev/null +++ b/packages/contracts/src/utils/web3_wrapper.ts @@ -0,0 +1,12 @@ +import { devConstants, web3Factory } from '@0xproject/dev-utils'; +import { Provider } from '@0xproject/types'; +import { Web3Wrapper } from '@0xproject/web3-wrapper'; + +export const txDefaults = { + from: devConstants.TESTRPC_FIRST_ADDRESS, + gas: devConstants.GAS_ESTIMATE, +}; +const providerConfigs = { shouldUseInProcessGanache: true }; +export const web3 = web3Factory.create(providerConfigs); +export const provider = web3.currentProvider; +export const web3Wrapper = new Web3Wrapper(provider); |