aboutsummaryrefslogtreecommitdiffstats
path: root/packages/order-utils/src/exchange_transfer_simulator.ts
diff options
context:
space:
mode:
authorFabio Berger <me@fabioberger.com>2018-06-12 01:21:32 +0800
committerFabio Berger <me@fabioberger.com>2018-06-12 01:21:32 +0800
commit21f7722f1023cc9d1848737b7c986f7df7a07122 (patch)
treea4b9c57fa75fb8c92bf44ed968df8b7112f478de /packages/order-utils/src/exchange_transfer_simulator.ts
parente4afe603f9c4dd5128f2446f9fe075516a3d4894 (diff)
downloaddexon-0x-contracts-21f7722f1023cc9d1848737b7c986f7df7a07122.tar.gz
dexon-0x-contracts-21f7722f1023cc9d1848737b7c986f7df7a07122.tar.zst
dexon-0x-contracts-21f7722f1023cc9d1848737b7c986f7df7a07122.zip
Move OrderValidationUtils (+ tests) and ExchangeTransferSimulator to order-utils
Diffstat (limited to 'packages/order-utils/src/exchange_transfer_simulator.ts')
-rw-r--r--packages/order-utils/src/exchange_transfer_simulator.ts114
1 files changed, 114 insertions, 0 deletions
diff --git a/packages/order-utils/src/exchange_transfer_simulator.ts b/packages/order-utils/src/exchange_transfer_simulator.ts
new file mode 100644
index 000000000..1cb132aba
--- /dev/null
+++ b/packages/order-utils/src/exchange_transfer_simulator.ts
@@ -0,0 +1,114 @@
+import { ExchangeContractErrs } from '@0xproject/types';
+import { BigNumber } from '@0xproject/utils';
+import { BlockParamLiteral } from 'ethereum-types';
+
+import { AbstractBalanceAndProxyAllowanceLazyStore } from './abstract/abstract_balance_and_proxy_allowance_lazy_store';
+import { constants } from './constants';
+import { TradeSide, TransferType } from './types';
+
+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 assetData Data of the asset being transferred. Includes
+ * it's identifying information and assetType,
+ * e.g address for ERC20, address & tokenId for ERC721
+ * @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(
+ assetData: 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(assetData, to, amountInBaseUnits);
+ return;
+ }
+ const balance = await this._store.getBalanceAsync(assetData, from);
+ const proxyAllowance = await this._store.getProxyAllowanceAsync(assetData, from);
+ if (proxyAllowance.lessThan(amountInBaseUnits)) {
+ ExchangeTransferSimulator._throwValidationError(FailureReason.ProxyAllowance, tradeSide, transferType);
+ }
+ if (balance.lessThan(amountInBaseUnits)) {
+ ExchangeTransferSimulator._throwValidationError(FailureReason.Balance, tradeSide, transferType);
+ }
+ await this._decreaseProxyAllowanceAsync(assetData, from, amountInBaseUnits);
+ await this._decreaseBalanceAsync(assetData, from, amountInBaseUnits);
+ await this._increaseBalanceAsync(assetData, to, amountInBaseUnits);
+ }
+ private async _decreaseProxyAllowanceAsync(
+ assetData: string,
+ userAddress: string,
+ amountInBaseUnits: BigNumber,
+ ): Promise<void> {
+ const proxyAllowance = await this._store.getProxyAllowanceAsync(assetData, userAddress);
+ if (!proxyAllowance.eq(constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS)) {
+ this._store.setProxyAllowance(assetData, userAddress, proxyAllowance.minus(amountInBaseUnits));
+ }
+ }
+ private async _increaseBalanceAsync(
+ assetData: string,
+ userAddress: string,
+ amountInBaseUnits: BigNumber,
+ ): Promise<void> {
+ const balance = await this._store.getBalanceAsync(assetData, userAddress);
+ this._store.setBalance(assetData, userAddress, balance.plus(amountInBaseUnits));
+ }
+ private async _decreaseBalanceAsync(
+ assetData: string,
+ userAddress: string,
+ amountInBaseUnits: BigNumber,
+ ): Promise<void> {
+ const balance = await this._store.getBalanceAsync(assetData, userAddress);
+ this._store.setBalance(assetData, userAddress, balance.minus(amountInBaseUnits));
+ }
+}