* move orderParsingUtils from order-utils to connect * Remove many functions from signatureUtils Removed from the exported object, that is. All of them are used in other existing code, so they were all moved to be as local to their usage as possible. * remove orderHashUtils.isValidOrderHash() * Move all *RevertErrors from order-utils... ...into their respective @0x/contracts- packages. * Refactor @0x/order-utils' orderHashUtils away - Move existing routines into @0x/contracts-test-utils - Migrate non-contract-test callers to a newly-exposed getOrderHash() method in DevUtils. * Move all *RevertErrors from @0x/utils... ...into their respective @0x/contracts- packages. * rm transactionHashUtils.isValidTransactionHash() * DevUtils.sol: Fail yarn test if too big to deploy * Refactor @0x/order-utils transactionHashUtils away - Move existing routines into @0x/contracts-test-utils - Migrate non-contract-test callers to a newly-exposed getTransactionHash() method in DevUtils. * Consolidate `Removed export...` CHANGELOG entries * Rm EthBalanceChecker from devutils wrapper exports * Stop importing from '.' or '.../src' * fix builds * fix prettier; dangling promise * increase max bundle size
178 lines
6.3 KiB
TypeScript
178 lines
6.3 KiB
TypeScript
import {
|
|
constants,
|
|
filterLogsToArguments,
|
|
orderHashUtils,
|
|
txDefaults as testTxDefaults,
|
|
} from '@0x/contracts-test-utils';
|
|
import { FillResults, Order, OrderInfo, SignatureType } from '@0x/types';
|
|
import { BigNumber } from '@0x/utils';
|
|
import { TxData, Web3Wrapper } from '@0x/web3-wrapper';
|
|
import * as crypto from 'crypto';
|
|
import { LogEntry } from 'ethereum-types';
|
|
|
|
import { artifacts } from '../artifacts';
|
|
|
|
import {
|
|
IsolatedExchangeContract,
|
|
IsolatedExchangeDispatchTransferFromCalledEventArgs as DispatchTransferFromCallArgs,
|
|
IsolatedExchangeFillEventArgs as FillEventArgs,
|
|
} from '../wrappers';
|
|
|
|
export interface AssetBalances {
|
|
[assetData: string]: { [address: string]: BigNumber };
|
|
}
|
|
|
|
export interface IsolatedExchangeEvents {
|
|
fillEvents: FillEventArgs[];
|
|
transferFromCalls: DispatchTransferFromCallArgs[];
|
|
}
|
|
|
|
export type Order = Order;
|
|
export type Numberish = string | number | BigNumber;
|
|
|
|
export const DEFAULT_GOOD_SIGNATURE = createGoodSignature();
|
|
export const DEFAULT_BAD_SIGNATURE = createBadSignature();
|
|
|
|
/**
|
|
* @dev Convenience wrapper for the `IsolatedExchange` contract.
|
|
*/
|
|
export class IsolatedExchangeWrapper {
|
|
public static readonly CHAIN_ID = 1337;
|
|
public readonly instance: IsolatedExchangeContract;
|
|
public lastTxEvents: IsolatedExchangeEvents = createEmptyEvents();
|
|
public lastTxBalanceChanges: AssetBalances = {};
|
|
|
|
public static async deployAsync(
|
|
web3Wrapper: Web3Wrapper,
|
|
txDefaults: Partial<TxData> = testTxDefaults,
|
|
): Promise<IsolatedExchangeWrapper> {
|
|
const provider = web3Wrapper.getProvider();
|
|
const instance = await IsolatedExchangeContract.deployFrom0xArtifactAsync(
|
|
artifacts.IsolatedExchange,
|
|
provider,
|
|
txDefaults,
|
|
{},
|
|
);
|
|
return new IsolatedExchangeWrapper(web3Wrapper, instance);
|
|
}
|
|
|
|
public static fromAddress(
|
|
address: string,
|
|
web3Wrapper: Web3Wrapper,
|
|
txDefaults: Partial<TxData> = testTxDefaults,
|
|
): IsolatedExchangeWrapper {
|
|
const provider = web3Wrapper.getProvider();
|
|
const instance = new IsolatedExchangeContract(address, provider, txDefaults);
|
|
return new IsolatedExchangeWrapper(web3Wrapper, instance);
|
|
}
|
|
|
|
public constructor(web3Wrapper: Web3Wrapper, instance: IsolatedExchangeContract) {
|
|
this.instance = instance;
|
|
}
|
|
|
|
public async getTakerAssetFilledAmountAsync(order: Order): Promise<BigNumber> {
|
|
return this.instance.filled(this.getOrderHash(order)).callAsync();
|
|
}
|
|
|
|
public async cancelOrderAsync(order: Order, txOpts?: TxData): Promise<void> {
|
|
await this.instance.cancelOrder(order).awaitTransactionSuccessAsync(txOpts);
|
|
}
|
|
|
|
public async cancelOrdersUpToAsync(epoch: BigNumber, txOpts?: TxData): Promise<void> {
|
|
await this.instance.cancelOrdersUpTo(epoch).awaitTransactionSuccessAsync(txOpts);
|
|
}
|
|
|
|
public async fillOrderAsync(
|
|
order: Order,
|
|
takerAssetFillAmount: Numberish,
|
|
signature: string = DEFAULT_GOOD_SIGNATURE,
|
|
txOpts?: TxData,
|
|
): Promise<FillResults> {
|
|
this.lastTxEvents = createEmptyEvents();
|
|
this.lastTxBalanceChanges = {};
|
|
const fillOrderFn = this.instance.fillOrder(order, new BigNumber(takerAssetFillAmount), signature);
|
|
const result = await fillOrderFn.callAsync();
|
|
const receipt = await fillOrderFn.awaitTransactionSuccessAsync(txOpts);
|
|
this.lastTxEvents = extractEvents(receipt.logs);
|
|
this.lastTxBalanceChanges = getBalanceChangesFromTransferFromCalls(this.lastTxEvents.transferFromCalls);
|
|
return result;
|
|
}
|
|
|
|
public getOrderHash(order: Order): string {
|
|
const domainInfo = {
|
|
exchangeAddress: this.instance.address,
|
|
chainId: IsolatedExchangeWrapper.CHAIN_ID,
|
|
};
|
|
return orderHashUtils.getOrderHashHex({ ...order, ...domainInfo });
|
|
}
|
|
|
|
public async getOrderInfoAsync(order: Order): Promise<OrderInfo> {
|
|
return this.instance.getOrderInfo(order).callAsync();
|
|
}
|
|
|
|
public getBalanceChange(assetData: string, address: string): BigNumber {
|
|
if (assetData in this.lastTxBalanceChanges) {
|
|
const balances = this.lastTxBalanceChanges[assetData];
|
|
if (address in balances) {
|
|
return balances[address];
|
|
}
|
|
}
|
|
return constants.ZERO_AMOUNT;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create a signature for the `IsolatedExchange` contract that will pass.
|
|
*/
|
|
export function createGoodSignature(type: SignatureType = SignatureType.EIP712): string {
|
|
return `0x01${Buffer.from([type]).toString('hex')}`;
|
|
}
|
|
|
|
/**
|
|
* Create a signature for the `IsolatedExchange` contract that will fail.
|
|
*/
|
|
export function createBadSignature(type: SignatureType = SignatureType.EIP712): string {
|
|
return `0x00${Buffer.from([type]).toString('hex')}`;
|
|
}
|
|
|
|
const ERC20_ASSET_DATA_LENGTH = 36;
|
|
|
|
/**
|
|
* Create asset data for the `IsolatedExchange` contract that will pass.
|
|
*/
|
|
export function createGoodAssetData(length: number = ERC20_ASSET_DATA_LENGTH): string {
|
|
return `0x01${crypto.randomBytes(length - 1).toString('hex')}`;
|
|
}
|
|
|
|
/**
|
|
* Create asset data for the `IsolatedExchange` contract that will fail.
|
|
*/
|
|
export function createBadAssetData(length: number = ERC20_ASSET_DATA_LENGTH): string {
|
|
return `0x00${crypto.randomBytes(length - 1).toString('hex')}`;
|
|
}
|
|
|
|
function createEmptyEvents(): IsolatedExchangeEvents {
|
|
return { fillEvents: [], transferFromCalls: [] };
|
|
}
|
|
|
|
function extractEvents(logs: LogEntry[]): IsolatedExchangeEvents {
|
|
return {
|
|
fillEvents: filterLogsToArguments<FillEventArgs>(logs, 'Fill'),
|
|
transferFromCalls: filterLogsToArguments<DispatchTransferFromCallArgs>(logs, 'DispatchTransferFromCalled'),
|
|
};
|
|
}
|
|
|
|
// Executes transferFrom calls to compute relative balances for addresses.
|
|
function getBalanceChangesFromTransferFromCalls(calls: DispatchTransferFromCallArgs[]): AssetBalances {
|
|
const changes: AssetBalances = {};
|
|
for (const call of calls) {
|
|
const { assetData, from, to, amount } = call;
|
|
const balances = (changes[assetData] = changes[assetData] || {});
|
|
const fromBalance = balances[from] || constants.ZERO_AMOUNT;
|
|
const toBalance = balances[to] || constants.ZERO_AMOUNT;
|
|
balances[from] = fromBalance.minus(amount);
|
|
balances[to] = toBalance.plus(amount);
|
|
}
|
|
return changes;
|
|
}
|