diff options
author | Leonid Logvinov <logvinov.leon@gmail.com> | 2018-07-05 21:04:01 +0800 |
---|---|---|
committer | Leonid Logvinov <logvinov.leon@gmail.com> | 2018-07-05 21:04:01 +0800 |
commit | 1ee2d6ed54b6159d1e8952692f4ddba0ebd65012 (patch) | |
tree | 1cebf47acf4fff2352a709035c141ec7ffdb8353 /packages/contract-wrappers/src | |
parent | 1eba78e20ac468d3b4d6ebe8fd91eb5277577e0a (diff) | |
parent | 5176d929fa6d3c6ce414448ea2441bd450f04e3c (diff) | |
download | dexon-sol-tools-1ee2d6ed54b6159d1e8952692f4ddba0ebd65012.tar.gz dexon-sol-tools-1ee2d6ed54b6159d1e8952692f4ddba0ebd65012.tar.zst dexon-sol-tools-1ee2d6ed54b6159d1e8952692f4ddba0ebd65012.zip |
Merge branch 'v2-prototype' into v2-contract-wrappers-WIP
Diffstat (limited to 'packages/contract-wrappers/src')
-rw-r--r-- | packages/contract-wrappers/src/contract_wrappers/contract_wrapper.ts | 13 | ||||
-rw-r--r-- | packages/contract-wrappers/src/utils/exchange_transfer_simulator.ts | 111 |
2 files changed, 122 insertions, 2 deletions
diff --git a/packages/contract-wrappers/src/contract_wrappers/contract_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/contract_wrapper.ts index a88745485..7895a42ee 100644 --- a/packages/contract-wrappers/src/contract_wrappers/contract_wrapper.ts +++ b/packages/contract-wrappers/src/contract_wrappers/contract_wrapper.ts @@ -2,7 +2,7 @@ import { ContractArtifact } from '@0xproject/sol-compiler'; import { AbiDecoder, intervalUtils } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import { BlockParamLiteral, ContractAbi, FilterObject, LogEntry, LogWithDecodedArgs, RawLog } from 'ethereum-types'; -import { Block, BlockAndLogStreamer } from 'ethereumjs-blockstream'; +import { Block, BlockAndLogStreamer, Log } from 'ethereumjs-blockstream'; import * as _ from 'lodash'; import { @@ -33,7 +33,7 @@ export abstract class ContractWrapper { public abstract abi: ContractAbi; protected _web3Wrapper: Web3Wrapper; protected _networkId: number; - private _blockAndLogStreamerIfExists?: BlockAndLogStreamer; + private _blockAndLogStreamerIfExists: BlockAndLogStreamer<Block, Log> | undefined; private _blockAndLogStreamIntervalIfExists?: NodeJS.Timer; private _filters: { [filterToken: string]: FilterObject }; private _filterCallbacks: { @@ -155,6 +155,7 @@ export abstract class ContractWrapper { this._blockAndLogStreamerIfExists = new BlockAndLogStreamer( this._web3Wrapper.getBlockAsync.bind(this._web3Wrapper), this._web3Wrapper.getLogsAsync.bind(this._web3Wrapper), + this._onBlockAndLogStreamerError.bind(this), ); const catchAllLogFilter = {}; this._blockAndLogStreamerIfExists.addLogFilter(catchAllLogFilter); @@ -172,6 +173,14 @@ export abstract class ContractWrapper { this._onLogStateChanged.bind(this, isRemoved), ); } + private _onBlockAndLogStreamerError(err: Error): void { + // Propogate all Blockstream subscriber errors to all + // top-level subscriptions + const filterCallbacks = _.values(this._filterCallbacks); + _.each(filterCallbacks, filterCallback => { + filterCallback(err); + }); + } private _onReconcileBlockError(err: Error): void { const filterTokens = _.keys(this._filterCallbacks); _.each(filterTokens, filterToken => { diff --git a/packages/contract-wrappers/src/utils/exchange_transfer_simulator.ts b/packages/contract-wrappers/src/utils/exchange_transfer_simulator.ts new file mode 100644 index 000000000..279f2a796 --- /dev/null +++ b/packages/contract-wrappers/src/utils/exchange_transfer_simulator.ts @@ -0,0 +1,111 @@ +import { ExchangeContractErrs } from '@0xproject/types'; +import { BigNumber } from '@0xproject/utils'; + +import { AbstractBalanceAndProxyAllowanceLazyStore } from '../abstract/abstract_balance_and_proxy_allowance_lazy_store'; +import { TradeSide, TransferType } from '../types'; +import { constants } from '../utils/constants'; + +enum FailureReason { + Balance = 'balance', + ProxyAllowance = 'proxyAllowance', +} + +const ERR_MSG_MAPPING = { + [FailureReason.Balance]: { + [TradeSide.Maker]: { + [TransferType.Trade]: ExchangeContractErrs.InsufficientMakerBalance, + [TransferType.Fee]: ExchangeContractErrs.InsufficientMakerFeeBalance, + }, + [TradeSide.Taker]: { + [TransferType.Trade]: ExchangeContractErrs.InsufficientTakerBalance, + [TransferType.Fee]: ExchangeContractErrs.InsufficientTakerFeeBalance, + }, + }, + [FailureReason.ProxyAllowance]: { + [TradeSide.Maker]: { + [TransferType.Trade]: ExchangeContractErrs.InsufficientMakerAllowance, + [TransferType.Fee]: ExchangeContractErrs.InsufficientMakerFeeAllowance, + }, + [TradeSide.Taker]: { + [TransferType.Trade]: ExchangeContractErrs.InsufficientTakerAllowance, + [TransferType.Fee]: ExchangeContractErrs.InsufficientTakerFeeAllowance, + }, + }, +}; + +export class ExchangeTransferSimulator { + private _store: AbstractBalanceAndProxyAllowanceLazyStore; + private static _throwValidationError( + failureReason: FailureReason, + tradeSide: TradeSide, + transferType: TransferType, + ): never { + const errMsg = ERR_MSG_MAPPING[failureReason][tradeSide][transferType]; + throw new Error(errMsg); + } + constructor(store: AbstractBalanceAndProxyAllowanceLazyStore) { + this._store = store; + } + /** + * Simulates transferFrom call performed by a proxy + * @param tokenAddress Address of the token to be transferred + * @param from Owner of the transferred tokens + * @param to Recipient of the transferred tokens + * @param amountInBaseUnits The amount of tokens being transferred + * @param tradeSide Is Maker/Taker transferring + * @param transferType Is it a fee payment or a value transfer + */ + public async transferFromAsync( + tokenAddress: string, + from: string, + to: string, + amountInBaseUnits: BigNumber, + tradeSide: TradeSide, + transferType: TransferType, + ): Promise<void> { + // HACK: When simulating an open order (e.g taker is NULL_ADDRESS), we don't want to adjust balances/ + // allowances for the taker. We do however, want to increase the balance of the maker since the maker + // might be relying on those funds to fill subsequent orders or pay the order's fees. + if (from === constants.NULL_ADDRESS && tradeSide === TradeSide.Taker) { + await this._increaseBalanceAsync(tokenAddress, to, amountInBaseUnits); + return; + } + const balance = await this._store.getBalanceAsync(tokenAddress, from); + const proxyAllowance = await this._store.getProxyAllowanceAsync(tokenAddress, from); + if (proxyAllowance.lessThan(amountInBaseUnits)) { + ExchangeTransferSimulator._throwValidationError(FailureReason.ProxyAllowance, tradeSide, transferType); + } + if (balance.lessThan(amountInBaseUnits)) { + ExchangeTransferSimulator._throwValidationError(FailureReason.Balance, tradeSide, transferType); + } + await this._decreaseProxyAllowanceAsync(tokenAddress, from, amountInBaseUnits); + await this._decreaseBalanceAsync(tokenAddress, from, amountInBaseUnits); + await this._increaseBalanceAsync(tokenAddress, to, amountInBaseUnits); + } + private async _decreaseProxyAllowanceAsync( + tokenAddress: string, + userAddress: string, + amountInBaseUnits: BigNumber, + ): Promise<void> { + const proxyAllowance = await this._store.getProxyAllowanceAsync(tokenAddress, userAddress); + if (!proxyAllowance.eq(constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS)) { + this._store.setProxyAllowance(tokenAddress, userAddress, proxyAllowance.minus(amountInBaseUnits)); + } + } + private async _increaseBalanceAsync( + tokenAddress: string, + userAddress: string, + amountInBaseUnits: BigNumber, + ): Promise<void> { + const balance = await this._store.getBalanceAsync(tokenAddress, userAddress); + this._store.setBalance(tokenAddress, userAddress, balance.plus(amountInBaseUnits)); + } + private async _decreaseBalanceAsync( + tokenAddress: string, + userAddress: string, + amountInBaseUnits: BigNumber, + ): Promise<void> { + const balance = await this._store.getBalanceAsync(tokenAddress, userAddress); + this._store.setBalance(tokenAddress, userAddress, balance.minus(amountInBaseUnits)); + } +} |