diff options
Diffstat (limited to 'packages/website')
18 files changed, 305 insertions, 911 deletions
diff --git a/packages/website/package.json b/packages/website/package.json index 878544d27..4c6299a4f 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -18,13 +18,14 @@ "author": "Fabio Berger", "license": "Apache-2.0", "dependencies": { - "@0xproject/contract-wrappers": "^0.0.5", - "@0xproject/order-utils": "^0.0.9", + "0x.js": "^0.38.6", + "@0xproject/contract-wrappers": "^1.0.1-rc.5", + "@0xproject/order-utils": "^1.0.1-rc.6", "@0xproject/react-docs": "^1.0.7", "@0xproject/react-shared": "^1.0.8", "@0xproject/subproviders": "^2.0.1", - "@0xproject/types": "^0.8.1", - "@0xproject/typescript-typings": "^0.4.3", + "@0xproject/types": "^1.0.1-rc.6", + "@0xproject/typescript-typings": "^1.0.5", "@0xproject/utils": "^1.0.7", "@0xproject/web3-wrapper": "^2.0.1", "accounting": "^0.4.1", @@ -32,6 +33,7 @@ "blockies": "^0.0.2", "bowser": "^1.9.3", "deep-equal": "^1.0.1", + "ethereum-types": "^1.0.5", "ethereumjs-util": "^5.1.1", "find-versions": "^2.0.0", "jsonschema": "^1.2.0", diff --git a/packages/website/ts/artifacts/Exchange.json b/packages/website/ts/artifacts/Exchange.json deleted file mode 100644 index af8db7360..000000000 --- a/packages/website/ts/artifacts/Exchange.json +++ /dev/null @@ -1,610 +0,0 @@ -{ - "contract_name": "Exchange", - "abi": [ - { - "constant": true, - "inputs": [ - { - "name": "numerator", - "type": "uint256" - }, - { - "name": "denominator", - "type": "uint256" - }, - { - "name": "target", - "type": "uint256" - } - ], - "name": "isRoundingError", - "outputs": [ - { - "name": "", - "type": "bool" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "", - "type": "bytes32" - } - ], - "name": "filled", - "outputs": [ - { - "name": "", - "type": "uint256" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "", - "type": "bytes32" - } - ], - "name": "cancelled", - "outputs": [ - { - "name": "", - "type": "uint256" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "orderAddresses", - "type": "address[5][]" - }, - { - "name": "orderValues", - "type": "uint256[6][]" - }, - { - "name": "fillTakerTokenAmount", - "type": "uint256" - }, - { - "name": "shouldThrowOnInsufficientBalanceOrAllowance", - "type": "bool" - }, - { - "name": "v", - "type": "uint8[]" - }, - { - "name": "r", - "type": "bytes32[]" - }, - { - "name": "s", - "type": "bytes32[]" - } - ], - "name": "fillOrdersUpTo", - "outputs": [ - { - "name": "", - "type": "uint256" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "orderAddresses", - "type": "address[5]" - }, - { - "name": "orderValues", - "type": "uint256[6]" - }, - { - "name": "cancelTakerTokenAmount", - "type": "uint256" - } - ], - "name": "cancelOrder", - "outputs": [ - { - "name": "", - "type": "uint256" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "ZRX_TOKEN_CONTRACT", - "outputs": [ - { - "name": "", - "type": "address" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "orderAddresses", - "type": "address[5][]" - }, - { - "name": "orderValues", - "type": "uint256[6][]" - }, - { - "name": "fillTakerTokenAmounts", - "type": "uint256[]" - }, - { - "name": "v", - "type": "uint8[]" - }, - { - "name": "r", - "type": "bytes32[]" - }, - { - "name": "s", - "type": "bytes32[]" - } - ], - "name": "batchFillOrKillOrders", - "outputs": [], - "payable": false, - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "orderAddresses", - "type": "address[5]" - }, - { - "name": "orderValues", - "type": "uint256[6]" - }, - { - "name": "fillTakerTokenAmount", - "type": "uint256" - }, - { - "name": "v", - "type": "uint8" - }, - { - "name": "r", - "type": "bytes32" - }, - { - "name": "s", - "type": "bytes32" - } - ], - "name": "fillOrKillOrder", - "outputs": [], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "orderHash", - "type": "bytes32" - } - ], - "name": "getUnavailableTakerTokenAmount", - "outputs": [ - { - "name": "", - "type": "uint256" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "signer", - "type": "address" - }, - { - "name": "hash", - "type": "bytes32" - }, - { - "name": "v", - "type": "uint8" - }, - { - "name": "r", - "type": "bytes32" - }, - { - "name": "s", - "type": "bytes32" - } - ], - "name": "isValidSignature", - "outputs": [ - { - "name": "", - "type": "bool" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "numerator", - "type": "uint256" - }, - { - "name": "denominator", - "type": "uint256" - }, - { - "name": "target", - "type": "uint256" - } - ], - "name": "getPartialAmount", - "outputs": [ - { - "name": "", - "type": "uint256" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "TOKEN_TRANSFER_PROXY_CONTRACT", - "outputs": [ - { - "name": "", - "type": "address" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "orderAddresses", - "type": "address[5][]" - }, - { - "name": "orderValues", - "type": "uint256[6][]" - }, - { - "name": "fillTakerTokenAmounts", - "type": "uint256[]" - }, - { - "name": "shouldThrowOnInsufficientBalanceOrAllowance", - "type": "bool" - }, - { - "name": "v", - "type": "uint8[]" - }, - { - "name": "r", - "type": "bytes32[]" - }, - { - "name": "s", - "type": "bytes32[]" - } - ], - "name": "batchFillOrders", - "outputs": [], - "payable": false, - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "orderAddresses", - "type": "address[5][]" - }, - { - "name": "orderValues", - "type": "uint256[6][]" - }, - { - "name": "cancelTakerTokenAmounts", - "type": "uint256[]" - } - ], - "name": "batchCancelOrders", - "outputs": [], - "payable": false, - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "orderAddresses", - "type": "address[5]" - }, - { - "name": "orderValues", - "type": "uint256[6]" - }, - { - "name": "fillTakerTokenAmount", - "type": "uint256" - }, - { - "name": "shouldThrowOnInsufficientBalanceOrAllowance", - "type": "bool" - }, - { - "name": "v", - "type": "uint8" - }, - { - "name": "r", - "type": "bytes32" - }, - { - "name": "s", - "type": "bytes32" - } - ], - "name": "fillOrder", - "outputs": [ - { - "name": "filledTakerTokenAmount", - "type": "uint256" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "orderAddresses", - "type": "address[5]" - }, - { - "name": "orderValues", - "type": "uint256[6]" - } - ], - "name": "getOrderHash", - "outputs": [ - { - "name": "", - "type": "bytes32" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "EXTERNAL_QUERY_GAS_LIMIT", - "outputs": [ - { - "name": "", - "type": "uint16" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "VERSION", - "outputs": [ - { - "name": "", - "type": "string" - } - ], - "payable": false, - "type": "function" - }, - { - "inputs": [ - { - "name": "_zrxToken", - "type": "address" - }, - { - "name": "_tokenTransferProxy", - "type": "address" - } - ], - "payable": false, - "type": "constructor" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "maker", - "type": "address" - }, - { - "indexed": false, - "name": "taker", - "type": "address" - }, - { - "indexed": true, - "name": "feeRecipient", - "type": "address" - }, - { - "indexed": false, - "name": "makerToken", - "type": "address" - }, - { - "indexed": false, - "name": "takerToken", - "type": "address" - }, - { - "indexed": false, - "name": "filledMakerTokenAmount", - "type": "uint256" - }, - { - "indexed": false, - "name": "filledTakerTokenAmount", - "type": "uint256" - }, - { - "indexed": false, - "name": "paidMakerFee", - "type": "uint256" - }, - { - "indexed": false, - "name": "paidTakerFee", - "type": "uint256" - }, - { - "indexed": true, - "name": "tokens", - "type": "bytes32" - }, - { - "indexed": false, - "name": "orderHash", - "type": "bytes32" - } - ], - "name": "LogFill", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "maker", - "type": "address" - }, - { - "indexed": true, - "name": "feeRecipient", - "type": "address" - }, - { - "indexed": false, - "name": "makerToken", - "type": "address" - }, - { - "indexed": false, - "name": "takerToken", - "type": "address" - }, - { - "indexed": false, - "name": "cancelledMakerTokenAmount", - "type": "uint256" - }, - { - "indexed": false, - "name": "cancelledTakerTokenAmount", - "type": "uint256" - }, - { - "indexed": true, - "name": "tokens", - "type": "bytes32" - }, - { - "indexed": false, - "name": "orderHash", - "type": "bytes32" - } - ], - "name": "LogCancel", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "errorId", - "type": "uint8" - }, - { - "indexed": true, - "name": "orderHash", - "type": "bytes32" - } - ], - "name": "LogError", - "type": "event" - } - ], - "networks": { - "1": { - "address": "0x12459c951127e0c374ff9105dda097662a027093" - }, - "3": { - "address": "0x479cc461fecd078f766ecc58533d6f69580cf3ac" - }, - "4": { - "address": "0x1d16ef40fac01cec8adac2ac49427b9384192c05" - }, - "42": { - "address": "0x90fe2af704b34e0224bf2299c838e04d4dcf1364" - }, - "50": { - "address": "0x48bacb9266a570d521063ef5dd96e61686dbe788" - } - } -} diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index 45994be5f..b97d86e99 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -1,15 +1,15 @@ +import { ZeroEx } from '0x.js'; import { BlockRange, ContractWrappers, DecodedLogEvent, - ExchangeContractEventArgs, + ExchangeCancelEventArgs, + ExchangeEventArgs, ExchangeEvents, + ExchangeFillEventArgs, IndexedFilterValues, - LogCancelContractEventArgs, - LogFillContractEventArgs, - Token as ZeroExToken, } from '@0xproject/contract-wrappers'; -import { isValidOrderHash, signOrderHashAsync } from '@0xproject/order-utils'; +import { assetDataUtils, orderHashUtils, signatureUtils, SignerType } from '@0xproject/order-utils'; import { EtherscanLinkSuffixes, utils as sharedUtils } from '@0xproject/react-shared'; import { ledgerEthereumBrowserClientFactoryAsync, @@ -19,21 +19,15 @@ import { SignerSubprovider, Web3ProviderEngine, } from '@0xproject/subproviders'; -import { - BlockParam, - ECSignature, - LogWithDecodedArgs, - Order, - Provider, - SignedOrder, - TransactionReceiptWithDecodedLogs, -} from '@0xproject/types'; +import { ECSignature, Order, SignedOrder, Token as ZeroExToken } from '@0xproject/types'; import { BigNumber, intervalUtils, logUtils, promisify } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; +import { BlockParam, LogWithDecodedArgs, Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types'; import * as _ from 'lodash'; import * as moment from 'moment'; import * as React from 'react'; import contract = require('truffle-contract'); + import { BlockchainWatcher } from 'ts/blockchain_watcher'; import { AssetSendCompleted } from 'ts/components/flash_messages/asset_send_completed'; import { TransactionSubmitted } from 'ts/components/flash_messages/transaction_submitted'; @@ -48,7 +42,7 @@ import { InjectedProviderObservable, InjectedProviderUpdate, InjectedWeb3, - Order as PortalOrder, + PortalOrder, Providers, ProviderType, Side, @@ -65,9 +59,6 @@ import FilterSubprovider = require('web3-provider-engine/subproviders/filters'); import * as MintableArtifacts from '../contracts/Mintable.json'; -// HACK: remove this hard-coded abi and use @0xproject/contract-wrappers -import * as Exchange from './artifacts/Exchange.json'; - const BLOCK_NUMBER_BACK_TRACK = 50; const GWEI_IN_WEI = 1000000000; @@ -83,6 +74,7 @@ export class Blockchain { public networkId: number; public nodeVersion: string; private _contractWrappers: ContractWrappers; + private _zeroEx: ZeroEx; private readonly _dispatcher: Dispatcher; private _web3Wrapper?: Web3Wrapper; private _blockchainWatcher?: BlockchainWatcher; @@ -230,8 +222,8 @@ export class Blockchain { } } public async isAddressInTokenRegistryAsync(tokenAddress: string): Promise<boolean> { - utils.assert(!_.isUndefined(this._contractWrappers), 'Contract Wrappers must be instantiated.'); - const tokenIfExists = await this._contractWrappers.tokenRegistry.getTokenIfExistsAsync(tokenAddress); + utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.'); + const tokenIfExists = await this._zeroEx.tokenRegistry.getTokenIfExistsAsync(tokenAddress); return !_.isUndefined(tokenIfExists); } public getLedgerDerivationPathIfExists(): string { @@ -266,7 +258,7 @@ export class Blockchain { utils.assert(!_.isUndefined(this._contractWrappers), 'Contract Wrappers must be instantiated.'); this._showFlashMessageIfLedger(); - const txHash = await this._contractWrappers.token.setProxyAllowanceAsync( + const txHash = await this._contractWrappers.erc20Token.setProxyAllowanceAsync( token.address, this._userAddressIfExists, amountInBaseUnits, @@ -307,7 +299,7 @@ export class Blockchain { utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses); this._showFlashMessageIfLedger(); - const txHash = await this._contractWrappers.token.transferAsync( + const txHash = await this._contractWrappers.erc20Token.transferAsync( token.address, this._userAddressIfExists, toAddress, @@ -332,66 +324,41 @@ export class Blockchain { }), ); } - public portalOrderToZeroExOrder(portalOrder: PortalOrder): SignedOrder { - const exchangeContractAddress = this.getExchangeContractAddressIfExists(); - const zeroExSignedOrder = { - exchangeContractAddress, - maker: portalOrder.signedOrder.maker, - taker: portalOrder.signedOrder.taker, - makerTokenAddress: portalOrder.signedOrder.makerTokenAddress, - takerTokenAddress: portalOrder.signedOrder.takerTokenAddress, - makerTokenAmount: new BigNumber(portalOrder.signedOrder.makerTokenAmount), - takerTokenAmount: new BigNumber(portalOrder.signedOrder.takerTokenAmount), - makerFee: new BigNumber(portalOrder.signedOrder.makerFee), - takerFee: new BigNumber(portalOrder.signedOrder.takerFee), - expirationUnixTimestampSec: new BigNumber(portalOrder.signedOrder.expirationUnixTimestampSec), - feeRecipient: portalOrder.signedOrder.feeRecipient, - ecSignature: portalOrder.signedOrder.ecSignature, - salt: new BigNumber(portalOrder.signedOrder.salt), - }; - return zeroExSignedOrder; - } public async fillOrderAsync(signedOrder: SignedOrder, fillTakerTokenAmount: BigNumber): Promise<BigNumber> { utils.assert(!_.isUndefined(this._contractWrappers), 'ContractWrappers must be instantiated.'); utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses); - - const shouldThrowOnInsufficientBalanceOrAllowance = true; - this._showFlashMessageIfLedger(); const txHash = await this._contractWrappers.exchange.fillOrderAsync( signedOrder, fillTakerTokenAmount, - shouldThrowOnInsufficientBalanceOrAllowance, this._userAddressIfExists, { gasPrice: this._defaultGasPrice, }, ); const receipt = await this._showEtherScanLinkAndAwaitTransactionMinedAsync(txHash); - const logs: Array<LogWithDecodedArgs<ExchangeContractEventArgs>> = receipt.logs as any; - this._contractWrappers.exchange.throwLogErrorsAsErrors(logs); - const logFill = _.find(logs, { event: 'LogFill' }); - const args = (logFill.args as any) as LogFillContractEventArgs; - const filledTakerTokenAmount = args.filledTakerTokenAmount; - return filledTakerTokenAmount; - } - public async cancelOrderAsync(signedOrder: SignedOrder, cancelTakerTokenAmount: BigNumber): Promise<BigNumber> { + const logs: Array<LogWithDecodedArgs<ExchangeEventArgs>> = receipt.logs as any; + const logFill = _.find(logs, { event: ExchangeEvents.Fill }); + const args = (logFill.args as any) as ExchangeFillEventArgs; + const takerAssetFilledAmount = args.takerAssetFilledAmount; + return takerAssetFilledAmount; + } + public async cancelOrderAsync(signedOrder: SignedOrder): Promise<string> { this._showFlashMessageIfLedger(); - const txHash = await this._contractWrappers.exchange.cancelOrderAsync(signedOrder, cancelTakerTokenAmount, { + const txHash = await this._contractWrappers.exchange.cancelOrderAsync(signedOrder, { gasPrice: this._defaultGasPrice, }); const receipt = await this._showEtherScanLinkAndAwaitTransactionMinedAsync(txHash); - const logs: Array<LogWithDecodedArgs<ExchangeContractEventArgs>> = receipt.logs as any; - this._contractWrappers.exchange.throwLogErrorsAsErrors(logs); - const logCancel = _.find(logs, { event: ExchangeEvents.LogCancel }); - const args = (logCancel.args as any) as LogCancelContractEventArgs; - const cancelledTakerTokenAmount = args.cancelledTakerTokenAmount; - return cancelledTakerTokenAmount; + const logs: Array<LogWithDecodedArgs<ExchangeEventArgs>> = receipt.logs as any; + const logCancel = _.find(logs, { event: ExchangeEvents.Cancel }); + const args = (logCancel.args as any) as ExchangeCancelEventArgs; + const cancelledOrderHash = args.orderHash; + return cancelledOrderHash; } public async getUnavailableTakerAmountAsync(orderHash: string): Promise<BigNumber> { - utils.assert(isValidOrderHash(orderHash), 'Must be valid orderHash'); + utils.assert(orderHashUtils.isValidOrderHash(orderHash), 'Must be valid orderHash'); utils.assert(!_.isUndefined(this._contractWrappers), 'ContractWrappers must be instantiated.'); - const unavailableTakerAmount = await this._contractWrappers.exchange.getUnavailableTakerAmountAsync(orderHash); + const unavailableTakerAmount = await this._contractWrappers.exchange.getFilledTakerAssetAmountAsync(orderHash); return unavailableTakerAmount; } public getExchangeContractAddressIfExists(): string | undefined { @@ -402,22 +369,27 @@ export class Blockchain { fillTakerTokenAmount: BigNumber, takerAddress: string, ): Promise<void> { - await this._contractWrappers.exchange.validateFillOrderThrowIfInvalidAsync( - signedOrder, - fillTakerTokenAmount, - takerAddress, - ); - } - public async validateCancelOrderThrowIfInvalidAsync( - order: Order, - cancelTakerTokenAmount: BigNumber, - ): Promise<void> { - await this._contractWrappers.exchange.validateCancelOrderThrowIfInvalidAsync(order, cancelTakerTokenAmount); + // TODO: add validation here + // we can use OrderValidationUtils here? + // await this._contractWrappers.exchange.validateFillOrderThrowIfInvalidAsync( + // signedOrder, + // fillTakerTokenAmount, + // takerAddress, + // ); } public isValidAddress(address: string): boolean { const lowercaseAddress = address.toLowerCase(); return Web3Wrapper.isAddress(lowercaseAddress); } + public async isValidSignatureAsync(data: string, signature: string, signerAddress: string): Promise<boolean> { + const result = await signatureUtils.isValidSignatureAsync( + this._contractWrappers.getProvider(), + data, + signature, + signerAddress, + ); + return result; + } public async pollTokenBalanceAsync(token: Token): Promise<BigNumber> { utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses); @@ -446,7 +418,7 @@ export class Blockchain { return newTokenBalancePromise; } - public async signOrderHashAsync(orderHash: string): Promise<ECSignature> { + public async signOrderHashAsync(orderHash: string): Promise<string> { utils.assert(!_.isUndefined(this._contractWrappers), 'ContractWrappers must be instantiated.'); const makerAddress = this._userAddressIfExists; // If makerAddress is undefined, this means they have a web3 instance injected into their browser @@ -454,20 +426,25 @@ export class Blockchain { if (_.isUndefined(makerAddress)) { throw new Error('Tried to send a sign request but user has no associated addresses'); } - this._showFlashMessageIfLedger(); - const nodeVersion = await this._web3Wrapper.getNodeVersionAsync(); - const isParityNode = utils.isParityNode(nodeVersion); - const isTestRpc = utils.isTestRpc(nodeVersion); - const isLedgerSigner = !_.isUndefined(this._ledgerSubprovider); - let shouldAddPersonalMessagePrefix = true; - if ((isParityNode && !isLedgerSigner) || isTestRpc || isLedgerSigner) { - shouldAddPersonalMessagePrefix = false; - } const provider = this._contractWrappers.getProvider(); - const ecSignature = await signOrderHashAsync(provider, orderHash, makerAddress, shouldAddPersonalMessagePrefix); - this._dispatcher.updateECSignature(ecSignature); - return ecSignature; + const isLedgerSigner = !_.isUndefined(this._ledgerSubprovider); + const injectedProvider = Blockchain._getInjectedWeb3().currentProvider; + const isMetaMaskSigner = utils.getProviderType(injectedProvider) === Providers.Metamask; + let signerType = SignerType.Default; + if (isLedgerSigner) { + signerType = SignerType.Ledger; + } else if (isMetaMaskSigner) { + signerType = SignerType.Metamask; + } + const ecSignatureString = await signatureUtils.ecSignOrderHashAsync( + provider, + orderHash, + makerAddress, + signerType, + ); + this._dispatcher.updateSignature(ecSignatureString); + return ecSignatureString; } public async mintTestTokensAsync(token: Token): Promise<void> { utils.assert(this._doesUserAddressExist(), BlockchainCallErrs.UserHasNoAssociatedAddresses); @@ -540,8 +517,8 @@ export class Blockchain { let allowance = new BigNumber(0); if (this._doesUserAddressExist()) { [balance, allowance] = await Promise.all([ - this._contractWrappers.token.getBalanceAsync(tokenAddress, ownerAddressIfExists), - this._contractWrappers.token.getProxyAllowanceAsync(tokenAddress, ownerAddressIfExists), + this._contractWrappers.erc20Token.getBalanceAsync(tokenAddress, ownerAddressIfExists), + this._contractWrappers.erc20Token.getProxyAllowanceAsync(tokenAddress, ownerAddressIfExists), ]); } return [balance, allowance]; @@ -653,8 +630,7 @@ export class Blockchain { ); const provider = this._contractWrappers.getProvider(); const web3Wrapper = new Web3Wrapper(provider); - // HACK: remove this hard-coded abi and use @0xproject/contract-wrappers - const exchangeAbi = _.get(Exchange, 'abi', []); + const exchangeAbi = this._contractWrappers.exchange.abi; web3Wrapper.abiDecoder.addABI(exchangeAbi); const receipt = await web3Wrapper.awaitTransactionSuccessAsync(txHash); return receipt; @@ -699,9 +675,9 @@ export class Blockchain { // Start a subscription for new logs this._contractWrappers.exchange.subscribe( - ExchangeEvents.LogFill, + ExchangeEvents.Fill, indexFilterValues, - async (err: Error, decodedLogEvent: DecodedLogEvent<LogFillContractEventArgs>) => { + async (err: Error, decodedLogEvent: DecodedLogEvent<ExchangeFillEventArgs>) => { if (err) { // Note: it's not entirely clear from the documentation which // errors will be thrown by `watch`. For now, let's log the error @@ -732,8 +708,8 @@ export class Blockchain { fromBlock, toBlock: 'latest' as BlockParam, }; - const decodedLogs = await this._contractWrappers.exchange.getLogsAsync<LogFillContractEventArgs>( - ExchangeEvents.LogFill, + const decodedLogs = await this._contractWrappers.exchange.getLogsAsync<ExchangeFillEventArgs>( + ExchangeEvents.Fill, blockRange, indexFilterValues, ); @@ -746,28 +722,28 @@ export class Blockchain { tradeHistoryStorage.addFillToUser(this._userAddressIfExists, this.networkId, fill); } } - private async _convertDecodedLogToFillAsync( - decodedLog: LogWithDecodedArgs<LogFillContractEventArgs>, - ): Promise<Fill> { + private async _convertDecodedLogToFillAsync(decodedLog: LogWithDecodedArgs<ExchangeFillEventArgs>): Promise<Fill> { const args = decodedLog.args; const blockTimestamp = await this._web3Wrapper.getBlockTimestampAsync(decodedLog.blockHash); + const makerToken = assetDataUtils.decodeERC20AssetData(args.makerAssetData).tokenAddress; + const takerToken = assetDataUtils.decodeERC20AssetData(args.takerAssetData).tokenAddress; const fill = { - filledTakerTokenAmount: args.filledTakerTokenAmount, - filledMakerTokenAmount: args.filledMakerTokenAmount, + filledTakerTokenAmount: args.takerAssetFilledAmount, + filledMakerTokenAmount: args.makerAssetFilledAmount, logIndex: decodedLog.logIndex, - maker: args.maker, + maker: args.makerAddress, orderHash: args.orderHash, - taker: args.taker, - makerToken: args.makerToken, - takerToken: args.takerToken, - paidMakerFee: args.paidMakerFee, - paidTakerFee: args.paidTakerFee, + taker: args.takerAddress, + makerToken, + takerToken, + paidMakerFee: args.makerFeePaid, + paidTakerFee: args.takerFeePaid, transactionHash: decodedLog.transactionHash, blockTimestamp, }; return fill; } - private _doesLogEventInvolveUser(decodedLog: LogWithDecodedArgs<LogFillContractEventArgs>): boolean { + private _doesLogEventInvolveUser(decodedLog: LogWithDecodedArgs<ExchangeFillEventArgs>): boolean { const args = decodedLog.args; const isUserMakerOrTaker = args.maker === this._userAddressIfExists || args.taker === this._userAddressIfExists; return isUserMakerOrTaker; @@ -796,8 +772,8 @@ export class Blockchain { if (this.networkId === constants.NETWORK_ID_MAINNET) { tokenRegistryTokens = await backendClient.getTokenInfosAsync(); } else { - utils.assert(!_.isUndefined(this._contractWrappers), 'ContractWrappers must be instantiated.'); - tokenRegistryTokens = await this._contractWrappers.tokenRegistry.getTokensAsync(); + utils.assert(!_.isUndefined(this._zeroEx), 'ZeroEx must be instantiated.'); + tokenRegistryTokens = await this._zeroEx.tokenRegistry.getTokensAsync(); } const tokenByAddress: TokenByAddress = {}; _.each(tokenRegistryTokens, (t: ZeroExToken) => { @@ -877,6 +853,11 @@ export class Blockchain { } else { this._contractWrappers = new ContractWrappers(provider, { networkId }); } + if (!_.isUndefined(this._zeroEx)) { + this._zeroEx.setProvider(provider, networkId); + } else { + this._zeroEx = new ZeroEx(provider, { networkId }); + } if (!_.isUndefined(this._blockchainWatcher)) { this._blockchainWatcher.destroy(); } diff --git a/packages/website/ts/components/fill_order.tsx b/packages/website/ts/components/fill_order.tsx index 7da2e0870..95f2039b3 100644 --- a/packages/website/ts/components/fill_order.tsx +++ b/packages/website/ts/components/fill_order.tsx @@ -1,4 +1,4 @@ -import { getOrderHashHex, isValidSignature } from '@0xproject/order-utils'; +import { assetDataUtils, orderHashUtils, signatureUtils } from '@0xproject/order-utils'; import { colors } from '@0xproject/react-shared'; import { Order as ZeroExOrder } from '@0xproject/types'; import { BigNumber, logUtils } from '@0xproject/utils'; @@ -22,10 +22,11 @@ import { VisualOrder } from 'ts/components/visual_order'; import { Dispatcher } from 'ts/redux/dispatcher'; import { portalOrderSchema } from 'ts/schemas/portal_order_schema'; import { validator } from 'ts/schemas/validator'; -import { AlertTypes, BlockchainErrs, Order, Token, TokenByAddress, WebsitePaths } from 'ts/types'; +import { AlertTypes, BlockchainErrs, PortalOrder, Token, TokenByAddress, WebsitePaths } from 'ts/types'; import { analytics } from 'ts/utils/analytics'; import { constants } from 'ts/utils/constants'; import { errorReporter } from 'ts/utils/error_reporter'; +import { orderParser } from 'ts/utils/order_parser'; import { utils } from 'ts/utils/utils'; interface FillOrderProps { @@ -36,7 +37,7 @@ interface FillOrderProps { networkId: number; userAddress: string; tokenByAddress: TokenByAddress; - initialOrder: Order; + initialOrder: PortalOrder; dispatcher: Dispatcher; lastForceTokenStateRefetch: number; isFullWidth?: boolean; @@ -49,7 +50,7 @@ interface FillOrderState { globalErrMsg: string; orderJSON: string; orderJSONErrMsg: string; - parsedOrder: Order; + parsedOrder: PortalOrder; didFillOrderSucceed: boolean; didCancelOrderSucceed: boolean; unavailableTakerAmount: BigNumber; @@ -191,16 +192,18 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> { ); } private _renderVisualOrder(): React.ReactNode { - const takerTokenAddress = this.state.parsedOrder.signedOrder.takerTokenAddress; + const takerTokenAddress = assetDataUtils.decodeERC20AssetData(this.state.parsedOrder.signedOrder.takerAssetData) + .tokenAddress; const takerToken = this.props.tokenByAddress[takerTokenAddress]; - const orderTakerAmount = new BigNumber(this.state.parsedOrder.signedOrder.takerTokenAmount); - const orderMakerAmount = new BigNumber(this.state.parsedOrder.signedOrder.makerTokenAmount); + const orderTakerAmount = this.state.parsedOrder.signedOrder.takerAssetAmount; + const orderMakerAmount = this.state.parsedOrder.signedOrder.makerAssetAmount; const takerAssetToken = { amount: orderTakerAmount.minus(this.state.unavailableTakerAmount), symbol: takerToken.symbol, }; const fillToken = this.props.tokenByAddress[takerTokenAddress]; - const makerTokenAddress = this.state.parsedOrder.signedOrder.makerTokenAddress; + const makerTokenAddress = assetDataUtils.decodeERC20AssetData(this.state.parsedOrder.signedOrder.makerAssetData) + .tokenAddress; const makerToken = this.props.tokenByAddress[makerTokenAddress]; const makerAssetToken = { amount: orderMakerAmount.times(takerAssetToken.amount).div(orderTakerAmount), @@ -210,7 +213,7 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> { amount: this.props.orderFillAmount, symbol: takerToken.symbol, }; - const parsedOrderExpiration = new BigNumber(this.state.parsedOrder.signedOrder.expirationUnixTimestampSec); + const parsedOrderExpiration = this.state.parsedOrder.signedOrder.expirationTimeSeconds; let orderReceiveAmount = 0; if (!_.isUndefined(this.props.orderFillAmount)) { @@ -222,7 +225,7 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> { } const isUserMaker = !_.isUndefined(this.state.parsedOrder) && - this.state.parsedOrder.signedOrder.maker === this.props.userAddress; + this.state.parsedOrder.signedOrder.makerAddress === this.props.userAddress; const expiryDate = utils.convertToReadableDateTimeFromUnixTimestamp(parsedOrderExpiration); return ( <div className="pt3 pb1"> @@ -233,11 +236,11 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> { Maker: </div> <div className="col col-2 pr1"> - <Identicon address={this.state.parsedOrder.signedOrder.maker} diameter={23} /> + <Identicon address={this.state.parsedOrder.signedOrder.makerAddress} diameter={23} /> </div> <div className="col col-6"> <EthereumAddress - address={this.state.parsedOrder.signedOrder.maker} + address={this.state.parsedOrder.signedOrder.makerAddress} networkId={this.props.networkId} /> </div> @@ -367,17 +370,19 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> { if (!_.isEmpty(this.state.orderJSONErrMsg)) { return; } - - const makerTokenIfExists = this.props.tokenByAddress[this.state.parsedOrder.signedOrder.makerTokenAddress]; - const takerTokenIfExists = this.props.tokenByAddress[this.state.parsedOrder.signedOrder.takerTokenAddress]; - + const makerTokenAddress = assetDataUtils.decodeERC20AssetData(this.state.parsedOrder.signedOrder.makerAssetData) + .tokenAddress; + const takerTokenAddress = assetDataUtils.decodeERC20AssetData(this.state.parsedOrder.signedOrder.takerAssetData) + .tokenAddress; + const makerTokenIfExists = this.props.tokenByAddress[makerTokenAddress]; + const takerTokenIfExists = this.props.tokenByAddress[takerTokenAddress]; const tokensToTrack: Token[] = []; const isUnseenMakerToken = _.isUndefined(makerTokenIfExists); const isMakerTokenTracked = !_.isUndefined(makerTokenIfExists) && utils.isTokenTracked(makerTokenIfExists); if (isUnseenMakerToken) { tokensToTrack.push({ ...this.state.parsedOrder.metadata.makerToken, - address: this.state.parsedOrder.signedOrder.makerTokenAddress, + address: makerTokenAddress, iconUrl: undefined, trackedTimestamp: undefined, isRegistered: false, @@ -390,7 +395,7 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> { if (isUnseenTakerToken) { tokensToTrack.push({ ...this.state.parsedOrder.metadata.takerToken, - address: this.state.parsedOrder.signedOrder.takerTokenAddress, + address: takerTokenAddress, iconUrl: undefined, trackedTimestamp: undefined, isRegistered: false, @@ -411,10 +416,10 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> { } private async _validateFillOrderFireAndForgetAsync(orderJSON: string): Promise<void> { let orderJSONErrMsg = ''; - let parsedOrder: Order; + let parsedOrder: PortalOrder; let orderHash: string; try { - const order = JSON.parse(orderJSON); + const order = orderParser.parseJsonString(orderJSON); const validationResult = validator.validate(order, portalOrderSchema); if (validationResult.errors.length > 0) { orderJSONErrMsg = 'Submitted order JSON is not a valid order'; @@ -422,36 +427,16 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> { return; } parsedOrder = order; - - const makerAmount = new BigNumber(parsedOrder.signedOrder.makerTokenAmount); - const takerAmount = new BigNumber(parsedOrder.signedOrder.takerTokenAmount); - const expiration = new BigNumber(parsedOrder.signedOrder.expirationUnixTimestampSec); - const salt = new BigNumber(parsedOrder.signedOrder.salt); - const parsedMakerFee = new BigNumber(parsedOrder.signedOrder.makerFee); - const parsedTakerFee = new BigNumber(parsedOrder.signedOrder.takerFee); - - const zeroExOrder: ZeroExOrder = { - exchangeContractAddress: parsedOrder.signedOrder.exchangeContractAddress, - expirationUnixTimestampSec: expiration, - feeRecipient: parsedOrder.signedOrder.feeRecipient, - maker: parsedOrder.signedOrder.maker, - makerFee: parsedMakerFee, - makerTokenAddress: parsedOrder.signedOrder.makerTokenAddress, - makerTokenAmount: makerAmount, - salt, - taker: _.isEmpty(parsedOrder.signedOrder.taker) - ? constants.NULL_ADDRESS - : parsedOrder.signedOrder.taker, - takerFee: parsedTakerFee, - takerTokenAddress: parsedOrder.signedOrder.takerTokenAddress, - takerTokenAmount: takerAmount, - }; - orderHash = getOrderHashHex(zeroExOrder); - + const signedOrder = parsedOrder.signedOrder; + orderHash = orderHashUtils.getOrderHashHex(signedOrder); const exchangeContractAddr = this.props.blockchain.getExchangeContractAddressIfExists(); - const signature = parsedOrder.signedOrder.ecSignature; - const isSignatureValid = isValidSignature(orderHash, signature, parsedOrder.signedOrder.maker); - if (exchangeContractAddr !== parsedOrder.signedOrder.exchangeContractAddress) { + const signature = signedOrder.signature; + const isSignatureValid = await this.props.blockchain.isValidSignatureAsync( + orderHash, + signature, + signedOrder.makerAddress, + ); + if (exchangeContractAddr !== signedOrder.exchangeAddress) { orderJSONErrMsg = 'This order was made on another network or using a deprecated Exchange contract'; parsedOrder = undefined; } else if (!isSignatureValid) { @@ -484,11 +469,15 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> { this.props.dispatcher.updateUserSuppliedOrderCache(undefined); } else { unavailableTakerAmount = await this.props.blockchain.getUnavailableTakerAmountAsync(orderHash); + const makerTokenAddress = assetDataUtils.decodeERC20AssetData(parsedOrder.signedOrder.makerAssetData) + .tokenAddress; + const takerTokenAddress = assetDataUtils.decodeERC20AssetData(parsedOrder.signedOrder.takerAssetData) + .tokenAddress; const isMakerTokenAddressInRegistry = await this.props.blockchain.isAddressInTokenRegistryAsync( - parsedOrder.signedOrder.makerTokenAddress, + makerTokenAddress, ); const isTakerTokenAddressInRegistry = await this.props.blockchain.isAddressInTokenRegistryAsync( - parsedOrder.signedOrder.takerTokenAddress, + takerTokenAddress, ); this.setState({ isMakerTokenAddressInRegistry, @@ -496,6 +485,7 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> { }); } + console.log(parsedOrder); this.setState({ didOrderValidationRun: true, orderJSON, @@ -537,7 +527,7 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> { globalErrMsg = 'You must specify a fill amount'; } - const signedOrder = this.props.blockchain.portalOrderToZeroExOrder(parsedOrder); + const signedOrder = parsedOrder.signedOrder; if (_.isEmpty(globalErrMsg)) { try { await this.props.blockchain.validateFillOrderThrowIfInvalidAsync( @@ -546,7 +536,7 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> { this.props.userAddress, ); } catch (err) { - globalErrMsg = utils.zeroExErrToHumanReadableErrMsg(err.message, parsedOrder.signedOrder.taker); + globalErrMsg = utils.zeroExErrToHumanReadableErrMsg(err.message, parsedOrder.signedOrder.takerAddress); } } if (!_.isEmpty(globalErrMsg)) { @@ -611,18 +601,11 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> { return; } let globalErrMsg = ''; - - const takerTokenAmount = new BigNumber(parsedOrder.signedOrder.takerTokenAmount); - - const signedOrder = this.props.blockchain.portalOrderToZeroExOrder(parsedOrder); - const orderHash = getOrderHashHex(signedOrder); + const signedOrder = parsedOrder.signedOrder; + const takerTokenAmount = signedOrder.takerAssetAmount; + const orderHash = orderHashUtils.getOrderHashHex(signedOrder); const unavailableTakerAmount = await this.props.blockchain.getUnavailableTakerAmountAsync(orderHash); const availableTakerTokenAmount = takerTokenAmount.minus(unavailableTakerAmount); - try { - await this.props.blockchain.validateCancelOrderThrowIfInvalidAsync(signedOrder, availableTakerTokenAmount); - } catch (err) { - globalErrMsg = utils.zeroExErrToHumanReadableErrMsg(err.message, parsedOrder.signedOrder.taker); - } if (!_.isEmpty(globalErrMsg)) { this.setState({ isCancelling: false, @@ -631,7 +614,7 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> { return; } try { - await this.props.blockchain.cancelOrderAsync(signedOrder, availableTakerTokenAmount); + await this.props.blockchain.cancelOrderAsync(signedOrder); this.setState({ isCancelling: false, didCancelOrderSucceed: true, diff --git a/packages/website/ts/components/fill_order_json.tsx b/packages/website/ts/components/fill_order_json.tsx index 90eedbb18..1ecc426bd 100644 --- a/packages/website/ts/components/fill_order_json.tsx +++ b/packages/website/ts/components/fill_order_json.tsx @@ -33,11 +33,7 @@ export class FillOrderJSON extends React.Component<FillOrderJSONProps, FillOrder }, }; const hintOrderExpiryTimestamp = utils.initialOrderExpiryUnixTimestampSec(); - const hintECSignature = { - r: '0xf01103f759e2289a28593eaf22e5820032...', - s: '937862111edcba395f8a9e0cc1b2c5e12320...', - v: 27, - }; + const hintECSignature = '0x012761a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33'; const hintSalt = generatePseudoRandomSalt(); const feeRecipient = constants.NULL_ADDRESS; const hintOrder = utils.generateOrder( diff --git a/packages/website/ts/components/generate_order/generate_order_form.tsx b/packages/website/ts/components/generate_order/generate_order_form.tsx index 72efab033..bb6265170 100644 --- a/packages/website/ts/components/generate_order/generate_order_form.tsx +++ b/packages/website/ts/components/generate_order/generate_order_form.tsx @@ -1,4 +1,4 @@ -import { generatePseudoRandomSalt, getOrderHashHex } from '@0xproject/order-utils'; +import { assetDataUtils, generatePseudoRandomSalt, orderHashUtils } from '@0xproject/order-utils'; import { colors } from '@0xproject/react-shared'; import { ECSignature, Order as ZeroExOrder } from '@0xproject/types'; import { BigNumber, logUtils } from '@0xproject/utils'; @@ -20,7 +20,16 @@ import { SwapIcon } from 'ts/components/ui/swap_icon'; import { Dispatcher } from 'ts/redux/dispatcher'; import { portalOrderSchema } from 'ts/schemas/portal_order_schema'; import { validator } from 'ts/schemas/validator'; -import { AlertTypes, BlockchainErrs, HashData, Order, Side, SideToAssetToken, Token, TokenByAddress } from 'ts/types'; +import { + AlertTypes, + BlockchainErrs, + HashData, + PortalOrder, + Side, + SideToAssetToken, + Token, + TokenByAddress, +} from 'ts/types'; import { analytics } from 'ts/utils/analytics'; import { constants } from 'ts/utils/constants'; import { errorReporter } from 'ts/utils/error_reporter'; @@ -41,7 +50,7 @@ interface GenerateOrderFormProps { orderExpiryTimestamp: BigNumber; networkId: number; userAddress: string; - orderECSignature: ECSignature; + orderSignature: string; orderTakerAddress: string; orderSalt: BigNumber; sideToAssetToken: SideToAssetToken; @@ -212,7 +221,7 @@ export class GenerateOrderForm extends React.Component<GenerateOrderFormProps, G <OrderJSON exchangeContractIfExists={exchangeContractIfExists} orderExpiryTimestamp={this.props.orderExpiryTimestamp} - orderECSignature={this.props.orderECSignature} + orderSignature={this.props.orderSignature} orderTakerAddress={this.props.orderTakerAddress} orderMakerAddress={this.props.userAddress} orderSalt={this.props.orderSalt} @@ -294,12 +303,12 @@ export class GenerateOrderForm extends React.Component<GenerateOrderFormProps, G return false; } } - private async _signTransactionAsync(): Promise<Order | undefined> { + private async _signTransactionAsync(): Promise<PortalOrder | undefined> { this.setState({ signingState: SigningState.SIGNING, }); - const exchangeContractAddr = this.props.blockchain.getExchangeContractAddressIfExists(); - if (_.isUndefined(exchangeContractAddr)) { + const exchangeAddress = this.props.blockchain.getExchangeContractAddressIfExists(); + if (_.isUndefined(exchangeAddress)) { this.props.dispatcher.updateShouldBlockchainErrDialogBeOpen(true); this.setState({ signingState: SigningState.UNSIGNED, @@ -308,28 +317,31 @@ export class GenerateOrderForm extends React.Component<GenerateOrderFormProps, G } const hashData = this.props.hashData; + const makerAssetData = assetDataUtils.encodeERC20AssetData(hashData.depositTokenContractAddr); + const takerAssetData = assetDataUtils.encodeERC20AssetData(hashData.receiveTokenContractAddr); const zeroExOrder: ZeroExOrder = { - exchangeContractAddress: exchangeContractAddr, - expirationUnixTimestampSec: hashData.orderExpiryTimestamp, - feeRecipient: hashData.feeRecipientAddress, - maker: hashData.orderMakerAddress, + senderAddress: constants.NULL_ADDRESS, + exchangeAddress, + expirationTimeSeconds: hashData.orderExpiryTimestamp, + feeRecipientAddress: hashData.feeRecipientAddress, + makerAddress: hashData.orderMakerAddress, makerFee: hashData.makerFee, - makerTokenAddress: hashData.depositTokenContractAddr, - makerTokenAmount: hashData.depositAmount, + makerAssetData, + makerAssetAmount: hashData.depositAmount, salt: hashData.orderSalt, - taker: hashData.orderTakerAddress, + takerAddress: hashData.orderTakerAddress, takerFee: hashData.takerFee, - takerTokenAddress: hashData.receiveTokenContractAddr, - takerTokenAmount: hashData.receiveAmount, + takerAssetData, + takerAssetAmount: hashData.receiveAmount, }; - const orderHash = getOrderHashHex(zeroExOrder); + const orderHash = orderHashUtils.getOrderHashHex(zeroExOrder); let globalErrMsg = ''; let order; try { - const ecSignature = await this.props.blockchain.signOrderHashAsync(orderHash); + const signature = await this.props.blockchain.signOrderHashAsync(orderHash); order = utils.generateOrder( - exchangeContractAddr, + exchangeAddress, this.props.sideToAssetToken, hashData.orderExpiryTimestamp, this.props.orderTakerAddress, @@ -337,10 +349,11 @@ export class GenerateOrderForm extends React.Component<GenerateOrderFormProps, G hashData.makerFee, hashData.takerFee, hashData.feeRecipientAddress, - ecSignature, + signature, this.props.tokenByAddress, hashData.orderSalt, ); + console.log(order); const validationResult = validator.validate(order, portalOrderSchema); if (validationResult.errors.length > 0) { globalErrMsg = 'Order signing failed. Please refresh and try again'; diff --git a/packages/website/ts/components/inputs/hash_input.tsx b/packages/website/ts/components/inputs/hash_input.tsx index 8d9cdfc0b..5125ec4de 100644 --- a/packages/website/ts/components/inputs/hash_input.tsx +++ b/packages/website/ts/components/inputs/hash_input.tsx @@ -1,9 +1,10 @@ -import { getOrderHashHex } from '@0xproject/order-utils'; +import { assetDataUtils, orderHashUtils } from '@0xproject/order-utils'; import { Styles } from '@0xproject/react-shared'; import { Order } from '@0xproject/types'; import * as _ from 'lodash'; import * as React from 'react'; import ReactTooltip = require('react-tooltip'); + import { Blockchain } from 'ts/blockchain'; import { FakeTextField } from 'ts/components/ui/fake_text_field'; import { HashData } from 'ts/types'; @@ -42,23 +43,26 @@ export class HashInput extends React.Component<HashInputProps, HashInputState> { ); } private _generateMessageHashHex(): string { - const exchangeContractAddress = this.props.blockchain.getExchangeContractAddressIfExists(); + const exchangeAddress = this.props.blockchain.getExchangeContractAddressIfExists(); const hashData = this.props.hashData; + const makerAssetData = assetDataUtils.encodeERC20AssetData(hashData.depositTokenContractAddr); + const takerAssetData = assetDataUtils.encodeERC20AssetData(hashData.receiveTokenContractAddr); const order: Order = { - exchangeContractAddress, - expirationUnixTimestampSec: hashData.orderExpiryTimestamp, - feeRecipient: hashData.feeRecipientAddress, - maker: _.isEmpty(hashData.orderMakerAddress) ? constants.NULL_ADDRESS : hashData.orderMakerAddress, + senderAddress: constants.NULL_ADDRESS, + exchangeAddress, + expirationTimeSeconds: hashData.orderExpiryTimestamp, + feeRecipientAddress: hashData.feeRecipientAddress, + makerAddress: _.isEmpty(hashData.orderMakerAddress) ? constants.NULL_ADDRESS : hashData.orderMakerAddress, makerFee: hashData.makerFee, - makerTokenAddress: hashData.depositTokenContractAddr, - makerTokenAmount: hashData.depositAmount, + makerAssetData, + makerAssetAmount: hashData.depositAmount, salt: hashData.orderSalt, - taker: hashData.orderTakerAddress, + takerAddress: hashData.orderTakerAddress, takerFee: hashData.takerFee, - takerTokenAddress: hashData.receiveTokenContractAddr, - takerTokenAmount: hashData.receiveAmount, + takerAssetData, + takerAssetAmount: hashData.receiveAmount, }; - const orderHash = getOrderHashHex(order); + const orderHash = orderHashUtils.getOrderHashHex(order); return orderHash; } } diff --git a/packages/website/ts/components/order_json.tsx b/packages/website/ts/components/order_json.tsx index cf06f10c8..2a4d992a0 100644 --- a/packages/website/ts/components/order_json.tsx +++ b/packages/website/ts/components/order_json.tsx @@ -14,7 +14,7 @@ import { utils } from 'ts/utils/utils'; interface OrderJSONProps { exchangeContractIfExists: string; orderExpiryTimestamp: BigNumber; - orderECSignature: ECSignature; + orderSignature: string; orderTakerAddress: string; orderMakerAddress: string; orderSalt: BigNumber; @@ -48,7 +48,7 @@ export class OrderJSON extends React.Component<OrderJSONProps, OrderJSONState> { this.props.orderMakerFee, this.props.orderTakerFee, this.props.orderFeeRecipient, - this.props.orderECSignature, + this.props.orderSignature, this.props.tokenByAddress, this.props.orderSalt, ); @@ -169,7 +169,7 @@ You can see and fill it here: ${this.state.shareLink}`); this.props.orderMakerFee, this.props.orderTakerFee, this.props.orderFeeRecipient, - this.props.orderECSignature, + this.props.orderSignature, this.props.tokenByAddress, this.props.orderSalt, ); diff --git a/packages/website/ts/components/portal/portal.tsx b/packages/website/ts/components/portal/portal.tsx index ff11880e3..b8d7ceea9 100644 --- a/packages/website/ts/components/portal/portal.tsx +++ b/packages/website/ts/components/portal/portal.tsx @@ -39,7 +39,7 @@ import { BlockchainErrs, HashData, ItemByAddress, - Order, + PortalOrder, ProviderType, ScreenWidths, Token, @@ -71,7 +71,7 @@ export interface PortalProps { userEtherBalanceInWei?: BigNumber; userAddress: string; shouldBlockchainErrDialogBeOpen: boolean; - userSuppliedOrderCache: Order; + userSuppliedOrderCache: PortalOrder; location: Location; flashMessage?: string | React.ReactNode; lastForceTokenStateRefetch: number; @@ -114,11 +114,11 @@ const DOCUMENT_DESCRIPTION = 'Learn about and trade on 0x Relayers'; export class Portal extends React.Component<PortalProps, PortalState> { private _blockchain: Blockchain; - private readonly _sharedOrderIfExists: Order; + private readonly _sharedOrderIfExists: PortalOrder; private readonly _throttledScreenWidthUpdate: () => void; constructor(props: PortalProps) { super(props); - this._sharedOrderIfExists = orderParser.parse(window.location.search); + this._sharedOrderIfExists = orderParser.parseQueryString(window.location.search); this._throttledScreenWidthUpdate = _.throttle(this._updateScreenWidth.bind(this), THROTTLE_TIMEOUT); const didAcceptPortalDisclaimer = localStorage.getItemIfExists(constants.LOCAL_STORAGE_KEY_ACCEPT_DISCLAIMER); const hasAcceptedDisclaimer = diff --git a/packages/website/ts/containers/generate_order_form.ts b/packages/website/ts/containers/generate_order_form.ts index 92296dbab..b2f74f0f8 100644 --- a/packages/website/ts/containers/generate_order_form.ts +++ b/packages/website/ts/containers/generate_order_form.ts @@ -20,7 +20,7 @@ interface ConnectedState { blockchainErr: BlockchainErrs; blockchainIsLoaded: boolean; orderExpiryTimestamp: BigNumber; - orderECSignature: ECSignature; + orderSignature: string; userAddress: string; orderTakerAddress: string; orderSalt: BigNumber; @@ -34,7 +34,7 @@ const mapStateToProps = (state: State, _ownProps: GenerateOrderFormProps): Conne blockchainErr: state.blockchainErr, blockchainIsLoaded: state.blockchainIsLoaded, orderExpiryTimestamp: state.orderExpiryTimestamp, - orderECSignature: state.orderECSignature, + orderSignature: state.orderSignature, orderTakerAddress: state.orderTakerAddress, orderSalt: state.orderSalt, networkId: state.networkId, diff --git a/packages/website/ts/containers/portal.ts b/packages/website/ts/containers/portal.ts index 6747cdf4e..4d7ff2f55 100644 --- a/packages/website/ts/containers/portal.ts +++ b/packages/website/ts/containers/portal.ts @@ -6,7 +6,7 @@ import { Dispatch } from 'redux'; import { Portal as PortalComponent, PortalProps as PortalComponentProps } from 'ts/components/portal/portal'; import { Dispatcher } from 'ts/redux/dispatcher'; import { State } from 'ts/redux/reducer'; -import { BlockchainErrs, HashData, Order, ProviderType, ScreenWidths, Side, TokenByAddress } from 'ts/types'; +import { BlockchainErrs, HashData, PortalOrder, ProviderType, ScreenWidths, Side, TokenByAddress } from 'ts/types'; import { constants } from 'ts/utils/constants'; import { Translate } from 'ts/utils/translate'; @@ -25,7 +25,7 @@ interface ConnectedState { screenWidth: ScreenWidths; shouldBlockchainErrDialogBeOpen: boolean; userAddress: string; - userSuppliedOrderCache: Order; + userSuppliedOrderCache: PortalOrder; flashMessage?: string | React.ReactNode; translate: Translate; isPortalOnboardingShowing: boolean; diff --git a/packages/website/ts/redux/dispatcher.ts b/packages/website/ts/redux/dispatcher.ts index db008d319..5e79b6518 100644 --- a/packages/website/ts/redux/dispatcher.ts +++ b/packages/website/ts/redux/dispatcher.ts @@ -7,7 +7,7 @@ import { AssetToken, BlockchainErrs, Language, - Order, + PortalOrder, ProviderType, ScreenWidths, Side, @@ -50,7 +50,7 @@ export class Dispatcher { type: ActionTypes.UpdateOrderSalt, }); } - public updateUserSuppliedOrderCache(order: Order): void { + public updateUserSuppliedOrderCache(order: PortalOrder): void { this._dispatch({ data: order, type: ActionTypes.UpdateUserSuppliedOrderCache, @@ -149,10 +149,10 @@ export class Dispatcher { type: ActionTypes.ForceTokenStateRefetch, }); } - public updateECSignature(ecSignature: ECSignature): void { + public updateSignature(signature: string): void { this._dispatch({ - data: ecSignature, - type: ActionTypes.UpdateOrderECSignature, + data: signature, + type: ActionTypes.UpdateOrderSignature, }); } public updateUserWeiBalance(balance?: BigNumber): void { diff --git a/packages/website/ts/redux/reducer.ts b/packages/website/ts/redux/reducer.ts index 1bc4611e0..99907e93e 100644 --- a/packages/website/ts/redux/reducer.ts +++ b/packages/website/ts/redux/reducer.ts @@ -7,7 +7,7 @@ import { Action, ActionTypes, BlockchainErrs, - Order, + PortalOrder, ProviderType, ScreenWidths, Side, @@ -31,7 +31,7 @@ export interface State { orderExpiryTimestamp: BigNumber; orderFillAmount: BigNumber; orderTakerAddress: string; - orderECSignature: ECSignature; + orderSignature: string; orderSalt: BigNumber; nodeVersion: string; screenWidth: ScreenWidths; @@ -45,7 +45,7 @@ export interface State { isPortalOnboardingShowing: boolean; hasPortalOnboardingBeenClosed: boolean; // Note: cache of supplied orderJSON in fill order step. Do not use for anything else. - userSuppliedOrderCache: Order; + userSuppliedOrderCache: PortalOrder; // Docs docsVersion: string; @@ -65,11 +65,7 @@ export const INITIAL_STATE: State = { networkId: undefined, orderExpiryTimestamp: utils.initialOrderExpiryUnixTimestampSec(), orderFillAmount: undefined, - orderECSignature: { - r: '', - s: '', - v: 27, - }, + orderSignature: '', orderTakerAddress: constants.NULL_ADDRESS, orderSalt: generatePseudoRandomSalt(), nodeVersion: undefined, @@ -90,7 +86,6 @@ export const INITIAL_STATE: State = { // Docs docsVersion: DEFAULT_DOCS_VERSION, availableDocVersions: [DEFAULT_DOCS_VERSION], - // Shared flashMessage: undefined, providerType: ProviderType.Injected, @@ -207,10 +202,10 @@ export function reducer(state: State = INITIAL_STATE, action: Action): State { lastForceTokenStateRefetch: moment().unix(), }; - case ActionTypes.UpdateOrderECSignature: { + case ActionTypes.UpdateOrderSignature: { return { ...state, - orderECSignature: action.data, + orderSignature: action.data, }; } diff --git a/packages/website/ts/schemas/portal_order_schema.ts b/packages/website/ts/schemas/portal_order_schema.ts index ea8aeabc6..15e61f5e4 100644 --- a/packages/website/ts/schemas/portal_order_schema.ts +++ b/packages/website/ts/schemas/portal_order_schema.ts @@ -1,7 +1,7 @@ export const portalOrderSchema = { id: '/PortalOrder', properties: { - signedOrder: { $ref: '/SignedOrder' }, + signedOrder: { $ref: '/signedOrderSchema' }, metadata: { $ref: '/OrderMetadata' }, }, required: ['signedOrder', 'metadata'], diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts index 6ab17e261..19cc8f858 100644 --- a/packages/website/ts/types.ts +++ b/packages/website/ts/types.ts @@ -1,4 +1,4 @@ -import { ECSignature } from '@0xproject/types'; +import { ECSignature, SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import { Provider } from 'ethereum-types'; import * as React from 'react'; @@ -56,28 +56,12 @@ export interface OrderToken { decimals: number; } -export interface SignedOrder { - maker: string; - taker: string; - makerTokenAddress: string; - takerTokenAddress: string; - makerFee: string; - takerFee: string; - makerTokenAmount: string; - takerTokenAmount: string; - expirationUnixTimestampSec: string; - feeRecipient: string; - salt: string; - ecSignature: ECSignature; - exchangeContractAddress: string; -} - export interface OrderMetadata { makerToken: OrderToken; takerToken: OrderToken; } -export interface Order { +export interface PortalOrder { signedOrder: SignedOrder; metadata: OrderMetadata; } @@ -120,7 +104,7 @@ export enum ActionTypes { UpdateChosenAssetTokenAddress = 'UPDATE_CHOSEN_ASSET_TOKEN_ADDRESS', UpdateOrderTakerAddress = 'UPDATE_ORDER_TAKER_ADDRESS', UpdateOrderSalt = 'UPDATE_ORDER_SALT', - UpdateOrderECSignature = 'UPDATE_ORDER_EC_SIGNATURE', + UpdateOrderSignature = 'UPDATE_ORDER_SIGNATURE', UpdateTokenByAddress = 'UPDATE_TOKEN_BY_ADDRESS', RemoveTokenFromTokenByAddress = 'REMOVE_TOKEN_FROM_TOKEN_BY_ADDRESS', ForceTokenStateRefetch = 'FORCE_TOKEN_STATE_REFETCH', diff --git a/packages/website/ts/utils/analytics.ts b/packages/website/ts/utils/analytics.ts index e5a1ddfa4..62dbff2b9 100644 --- a/packages/website/ts/utils/analytics.ts +++ b/packages/website/ts/utils/analytics.ts @@ -1,5 +1,6 @@ +import { assetDataUtils } from '@0xproject/order-utils'; import * as _ from 'lodash'; -import { ObjectMap, Order } from 'ts/types'; +import { ObjectMap, PortalOrder } from 'ts/types'; import { utils } from 'ts/utils/utils'; export interface HeapAnalytics { @@ -53,12 +54,16 @@ export class Analytics { } // tslint:enable:no-floating-promises // Custom methods - public trackOrderEvent(eventName: string, order: Order): void { + public trackOrderEvent(eventName: string, order: PortalOrder): void { + const takerTokenAmount = order.signedOrder.takerAssetAmount.toString(); + const makerTokenAmount = order.signedOrder.makerAssetAmount.toString(); + const takerToken = assetDataUtils.decodeERC20AssetData(order.signedOrder.takerAssetData).tokenAddress; + const makerToken = assetDataUtils.decodeERC20AssetData(order.signedOrder.makerAssetData).tokenAddress; const orderLoggingData = { - takerTokenAmount: order.signedOrder.takerTokenAmount, - makeTokenAmount: order.signedOrder.makerTokenAmount, - takerToken: order.metadata.takerToken.symbol, - makerToken: order.metadata.makerToken.symbol, + takerTokenAmount, + makerTokenAmount, + takerToken, + makerToken, }; this.track(eventName, orderLoggingData); } diff --git a/packages/website/ts/utils/order_parser.ts b/packages/website/ts/utils/order_parser.ts index be08da80e..2e1d4d2f2 100644 --- a/packages/website/ts/utils/order_parser.ts +++ b/packages/website/ts/utils/order_parser.ts @@ -1,12 +1,12 @@ -import { logUtils } from '@0xproject/utils'; +import { BigNumber, logUtils } from '@0xproject/utils'; import * as _ from 'lodash'; import { portalOrderSchema } from 'ts/schemas/portal_order_schema'; import { validator } from 'ts/schemas/validator'; -import { Order } from 'ts/types'; +import { PortalOrder } from 'ts/types'; export const orderParser = { - parse(queryString: string): Order | undefined { + parseQueryString(queryString: string): PortalOrder | undefined { if (queryString.length === 0) { return undefined; } @@ -28,6 +28,48 @@ export const orderParser = { logUtils.log(`Invalid shared order: ${validationResult.errors}`); return undefined; } - return order; + const signedOrder = _.get(order, 'signedOrder'); + const convertedSignedOrder = convertOrderStringFieldsToBigNumber(signedOrder); + const result = { + ...order, + signedOrder: convertedSignedOrder, + }; + return result; + }, + parseJsonString(orderJson: string): PortalOrder { + const order = JSON.parse(orderJson); + const signedOrder = _.get(order, 'signedOrder'); + const convertedSignedOrder = convertOrderStringFieldsToBigNumber(signedOrder); + const result = { + ...order, + signedOrder: convertedSignedOrder, + }; + return result; }, }; + +// TODO: consolidate this function with that in typeConverters in @0xproject/connect +function convertOrderStringFieldsToBigNumber(order: any): any { + return convertStringsFieldsToBigNumbers(order, [ + 'makerAssetAmount', + 'takerAssetAmount', + 'makerFee', + 'takerFee', + 'expirationTimeSeconds', + 'salt', + ]); +} + +// TODO: consolidate this function with that in typeConverters in @0xproject/connect +function convertStringsFieldsToBigNumbers(obj: any, fields: string[]): any { + const result = _.assign({}, obj); + _.each(fields, field => { + _.update(result, field, (value: string) => { + if (_.isUndefined(value)) { + throw new Error(`Could not find field '${field}' while converting string fields to BigNumber.`); + } + return new BigNumber(value); + }); + }); + return result; +} diff --git a/packages/website/ts/utils/utils.ts b/packages/website/ts/utils/utils.ts index 39bbd404c..eda0e513d 100644 --- a/packages/website/ts/utils/utils.ts +++ b/packages/website/ts/utils/utils.ts @@ -1,11 +1,12 @@ -import { ContractWrappersError, ExchangeContractErrs } from '@0xproject/contract-wrappers'; -import { OrderError } from '@0xproject/order-utils'; +import { ContractWrappersError } from '@0xproject/contract-wrappers'; +import { assetDataUtils, OrderError } from '@0xproject/order-utils'; import { constants as sharedConstants, Networks } from '@0xproject/react-shared'; -import { ECSignature, Provider } from '@0xproject/types'; +import { ECSignature, ExchangeContractErrs } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import * as bowser from 'bowser'; import deepEqual = require('deep-equal'); +import { Provider } from 'ethereum-types'; import * as _ from 'lodash'; import * as moment from 'moment'; import * as numeral from 'numeral'; @@ -16,7 +17,7 @@ import { BrowserType, Environments, OperatingSystemType, - Order, + PortalOrder, Providers, ProviderType, ScreenWidths, @@ -64,35 +65,36 @@ export const utils = { return formattedDate; }, generateOrder( - exchangeContractAddress: string, + exchangeAddress: string, sideToAssetToken: SideToAssetToken, - expirationUnixTimestampSec: BigNumber, + expirationTimeSeconds: BigNumber, orderTakerAddress: string, orderMakerAddress: string, makerFee: BigNumber, takerFee: BigNumber, - feeRecipient: string, - ecSignature: ECSignature, + feeRecipientAddress: string, + signature: string, tokenByAddress: TokenByAddress, orderSalt: BigNumber, - ): Order { + ): PortalOrder { const makerToken = tokenByAddress[sideToAssetToken[Side.Deposit].address]; const takerToken = tokenByAddress[sideToAssetToken[Side.Receive].address]; const order = { signedOrder: { - maker: orderMakerAddress, - taker: orderTakerAddress, - makerFee: makerFee.toString(), - takerFee: takerFee.toString(), - makerTokenAmount: sideToAssetToken[Side.Deposit].amount.toString(), - takerTokenAmount: sideToAssetToken[Side.Receive].amount.toString(), - makerTokenAddress: makerToken.address, - takerTokenAddress: takerToken.address, - expirationUnixTimestampSec: expirationUnixTimestampSec.toString(), - feeRecipient, - salt: orderSalt.toString(), - ecSignature, - exchangeContractAddress, + senderAddress: constants.NULL_ADDRESS, + makerAddress: orderMakerAddress, + takerAddress: orderTakerAddress, + makerFee, + takerFee, + makerAssetAmount: sideToAssetToken[Side.Deposit].amount, + takerAssetAmount: sideToAssetToken[Side.Receive].amount, + makerAssetData: assetDataUtils.encodeERC20AssetData(makerToken.address), + takerAssetData: assetDataUtils.encodeERC20AssetData(takerToken.address), + expirationTimeSeconds, + feeRecipientAddress, + salt: orderSalt, + signature, + exchangeAddress, }, metadata: { makerToken: { @@ -231,10 +233,10 @@ export const utils = { const ContractWrappersErrorToHumanReadableError: { [error: string]: string } = { [ContractWrappersError.ExchangeContractDoesNotExist]: 'Exchange contract does not exist', [ContractWrappersError.EtherTokenContractDoesNotExist]: 'EtherToken contract does not exist', - [ContractWrappersError.TokenTransferProxyContractDoesNotExist]: - 'TokenTransferProxy contract does not exist', - [ContractWrappersError.TokenRegistryContractDoesNotExist]: 'TokenRegistry contract does not exist', - [ContractWrappersError.TokenContractDoesNotExist]: 'Token contract does not exist', + [ContractWrappersError.ERC20ProxyContractDoesNotExist]: 'ERC20 proxy contract des not exist', + [ContractWrappersError.ERC721ProxyContractDoesNotExist]: 'ERC721 proxy contract des not exist', + [ContractWrappersError.ERC20TokenContractDoesNotExist]: 'ERC20 token contract does not exist', + [ContractWrappersError.ERC721TokenContractDoesNotExist]: 'ERC721 token contract does not exist', [ContractWrappersError.ZRXContractDoesNotExist]: 'ZRX contract does not exist', [BlockchainCallErrs.UserHasNoAssociatedAddresses]: 'User has no addresses available', [OrderError.InvalidSignature]: 'Order signature is not valid', @@ -247,12 +249,9 @@ export const utils = { } = { [ExchangeContractErrs.OrderFillExpired]: 'This order has expired', [ExchangeContractErrs.OrderCancelExpired]: 'This order has expired', - [ExchangeContractErrs.OrderCancelAmountZero]: "Order cancel amount can't be 0", - [ExchangeContractErrs.OrderAlreadyCancelledOrFilled]: - 'This order has already been completely filled or cancelled', + [ExchangeContractErrs.OrderCancelled]: 'This order has been cancelled', [ExchangeContractErrs.OrderFillAmountZero]: "Order fill amount can't be 0", - [ExchangeContractErrs.OrderRemainingFillAmountZero]: - 'This order has already been completely filled or cancelled', + [ExchangeContractErrs.OrderRemainingFillAmountZero]: 'This order has already been completely filled', [ExchangeContractErrs.OrderFillRoundingError]: 'Rounding error will occur when filling this order. Please try filling a different amount.', [ExchangeContractErrs.InsufficientTakerBalance]: |