forwarder test factory first pass (supports orders with no fees and percentage fees)
This commit is contained in:
		@@ -0,0 +1,156 @@
 | 
				
			|||||||
 | 
					import { ERC20Wrapper, ERC721Wrapper } from '@0x/contracts-asset-proxy';
 | 
				
			||||||
 | 
					import { chaiSetup, constants, ERC20BalancesByOwner, web3Wrapper } from '@0x/contracts-test-utils';
 | 
				
			||||||
 | 
					import { SignedOrder } from '@0x/types';
 | 
				
			||||||
 | 
					import { BigNumber } from '@0x/utils';
 | 
				
			||||||
 | 
					import * as chai from 'chai';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { ForwarderWrapper } from './forwarder_wrapper';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					chaiSetup.configure();
 | 
				
			||||||
 | 
					const expect = chai.expect;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO: move these somewhere?
 | 
				
			||||||
 | 
					const enum OrderFee {
 | 
				
			||||||
 | 
					    NoFee,
 | 
				
			||||||
 | 
					    Percentage,
 | 
				
			||||||
 | 
					    Weth,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface ForwarderFillState {
 | 
				
			||||||
 | 
					    takerAssetFillAmount: BigNumber;
 | 
				
			||||||
 | 
					    makerAssetFillAmount: BigNumber;
 | 
				
			||||||
 | 
					    wethFees: BigNumber;
 | 
				
			||||||
 | 
					    percentageFees: BigNumber;
 | 
				
			||||||
 | 
					    maxOversoldWeth: BigNumber;
 | 
				
			||||||
 | 
					    maxOverboughtMakerAsset: BigNumber;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function getFeeType(order: SignedOrder): OrderFee {
 | 
				
			||||||
 | 
					    if (order.takerFee.eq(new BigNumber(0))) {
 | 
				
			||||||
 | 
					        return OrderFee.NoFee;
 | 
				
			||||||
 | 
					    } else if (order.takerFeeAssetData === order.makerAssetData) {
 | 
				
			||||||
 | 
					        return OrderFee.Percentage;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        return OrderFee.Weth;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class ForwarderTestFactory {
 | 
				
			||||||
 | 
					    private readonly _forwarderWrapper: ForwarderWrapper;
 | 
				
			||||||
 | 
					    private readonly _erc20Wrapper: ERC20Wrapper;
 | 
				
			||||||
 | 
					    private readonly _forwarderAddress: string;
 | 
				
			||||||
 | 
					    private readonly _makerAddress: string;
 | 
				
			||||||
 | 
					    private readonly _takerAddress: string;
 | 
				
			||||||
 | 
					    private readonly _orderFeeRecipientAddress: string;
 | 
				
			||||||
 | 
					    private readonly _forwarderFeeRecipientAddress: string;
 | 
				
			||||||
 | 
					    private readonly _gasPrice: BigNumber;
 | 
				
			||||||
 | 
					    private readonly _wethAddress: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(
 | 
				
			||||||
 | 
					        forwarderWrapper: ForwarderWrapper,
 | 
				
			||||||
 | 
					        erc20Wrapper: ERC20Wrapper,
 | 
				
			||||||
 | 
					        forwarderAddress: string,
 | 
				
			||||||
 | 
					        makerAddress: string,
 | 
				
			||||||
 | 
					        takerAddress: string,
 | 
				
			||||||
 | 
					        orderFeeRecipientAddress: string,
 | 
				
			||||||
 | 
					        forwarderFeeRecipientAddress: string,
 | 
				
			||||||
 | 
					        gasPrice: BigNumber,
 | 
				
			||||||
 | 
					        wethAddress: string,
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					        this._forwarderWrapper = forwarderWrapper;
 | 
				
			||||||
 | 
					        this._erc20Wrapper = erc20Wrapper;
 | 
				
			||||||
 | 
					        this._forwarderAddress = forwarderAddress;
 | 
				
			||||||
 | 
					        this._makerAddress = makerAddress;
 | 
				
			||||||
 | 
					        this._takerAddress = takerAddress;
 | 
				
			||||||
 | 
					        this._orderFeeRecipientAddress = orderFeeRecipientAddress;
 | 
				
			||||||
 | 
					        this._forwarderFeeRecipientAddress = forwarderFeeRecipientAddress;
 | 
				
			||||||
 | 
					        this._gasPrice = gasPrice;
 | 
				
			||||||
 | 
					        this._wethAddress = wethAddress;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // TODO: different assets (ERC721), rounding, forwarder fees
 | 
				
			||||||
 | 
					    public async generateTestAsync(
 | 
				
			||||||
 | 
					        orders: SignedOrder[],
 | 
				
			||||||
 | 
					        makerAssetAddress: string,
 | 
				
			||||||
 | 
					        takerEthBalanceBefore: BigNumber,
 | 
				
			||||||
 | 
					        erc20Balances: ERC20BalancesByOwner,
 | 
				
			||||||
 | 
					    ): Promise<void> {
 | 
				
			||||||
 | 
					        // Simulates filling all but the last order, which will be partially filled
 | 
				
			||||||
 | 
					        const expectedResults = orders
 | 
				
			||||||
 | 
					            .reduce(
 | 
				
			||||||
 | 
					                (prev: ForwarderFillState, order: SignedOrder, currentIndex: number) => {
 | 
				
			||||||
 | 
					                    const current = { ...prev };
 | 
				
			||||||
 | 
					                    const { makerAssetAmount, takerAssetAmount, takerFee } = currentIndex === (orders.length - 1) ?
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            makerAssetAmount: order.makerAssetAmount.dividedToIntegerBy(2),
 | 
				
			||||||
 | 
					                            takerAssetAmount: order.takerAssetAmount.dividedToIntegerBy(2),
 | 
				
			||||||
 | 
					                            takerFee: order.takerFee.dividedToIntegerBy(2),
 | 
				
			||||||
 | 
					                        } : order;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    current.takerAssetFillAmount = current.takerAssetFillAmount.plus(
 | 
				
			||||||
 | 
					                        takerAssetAmount,
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					                    current.makerAssetFillAmount = current.makerAssetFillAmount.plus(
 | 
				
			||||||
 | 
					                        makerAssetAmount,
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    switch (getFeeType(order)) {
 | 
				
			||||||
 | 
					                        case OrderFee.Percentage:
 | 
				
			||||||
 | 
					                            current.percentageFees = current.percentageFees.plus(takerFee);
 | 
				
			||||||
 | 
					                            break;
 | 
				
			||||||
 | 
					                        case OrderFee.Weth:
 | 
				
			||||||
 | 
					                            current.wethFees = current.wethFees.plus(takerFee);
 | 
				
			||||||
 | 
					                            current.maxOversoldWeth = current.maxOversoldWeth.plus(new BigNumber(1));
 | 
				
			||||||
 | 
					                            current.maxOverboughtMakerAsset = current.maxOversoldWeth
 | 
				
			||||||
 | 
					                                .times(makerAssetAmount)
 | 
				
			||||||
 | 
					                                .dividedToIntegerBy(takerAssetAmount);
 | 
				
			||||||
 | 
					                            break;
 | 
				
			||||||
 | 
					                        default:
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    return current;
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    takerAssetFillAmount: new BigNumber(0),
 | 
				
			||||||
 | 
					                    makerAssetFillAmount: new BigNumber(0),
 | 
				
			||||||
 | 
					                    wethFees: new BigNumber(0),
 | 
				
			||||||
 | 
					                    percentageFees: new BigNumber(0),
 | 
				
			||||||
 | 
					                    maxOversoldWeth: new BigNumber(0),
 | 
				
			||||||
 | 
					                    maxOverboughtMakerAsset: new BigNumber(0),
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const ethValue = expectedResults.takerAssetFillAmount.plus(expectedResults.wethFees);
 | 
				
			||||||
 | 
					        const tx = await this._forwarderWrapper.marketSellOrdersWithEthAsync(orders, {
 | 
				
			||||||
 | 
					            value: ethValue,
 | 
				
			||||||
 | 
					            from: this._takerAddress,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        const totalEthSpent = ethValue.plus(this._gasPrice.times(tx.gasUsed));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const takerEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(this._takerAddress);
 | 
				
			||||||
 | 
					        const forwarderEthBalance = await web3Wrapper.getBalanceInWeiAsync(this._forwarderAddress);
 | 
				
			||||||
 | 
					        const newBalances = await this._erc20Wrapper.getBalancesAsync();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        expect(newBalances[this._makerAddress][makerAssetAddress]).to.be.bignumber.equal(
 | 
				
			||||||
 | 
					            erc20Balances[this._makerAddress][makerAssetAddress].minus(expectedResults.makerAssetFillAmount),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        expect(newBalances[this._takerAddress][makerAssetAddress]).to.be.bignumber.equal(
 | 
				
			||||||
 | 
					            erc20Balances[this._takerAddress][makerAssetAddress].plus(expectedResults.makerAssetFillAmount).minus(expectedResults.percentageFees),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        expect(newBalances[this._orderFeeRecipientAddress][makerAssetAddress]).to.be.bignumber.equal(
 | 
				
			||||||
 | 
					            erc20Balances[this._orderFeeRecipientAddress][makerAssetAddress].plus(expectedResults.percentageFees),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        expect(newBalances[this._makerAddress][this._wethAddress]).to.be.bignumber.equal(
 | 
				
			||||||
 | 
					            erc20Balances[this._makerAddress][this._wethAddress].plus(expectedResults.takerAssetFillAmount).minus(expectedResults.wethFees),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        expect(newBalances[this._orderFeeRecipientAddress][this._wethAddress]).to.be.bignumber.equal(
 | 
				
			||||||
 | 
					            erc20Balances[this._orderFeeRecipientAddress][this._wethAddress].plus(expectedResults.wethFees),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        expect(newBalances[this._forwarderAddress][this._wethAddress]).to.be.bignumber.equal(constants.ZERO_AMOUNT);
 | 
				
			||||||
 | 
					        expect(newBalances[this._forwarderAddress][makerAssetAddress]).to.be.bignumber.equal(constants.ZERO_AMOUNT);
 | 
				
			||||||
 | 
					        expect(forwarderEthBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1 +1,2 @@
 | 
				
			|||||||
export * from './forwarder_wrapper';
 | 
					export * from './forwarder_wrapper';
 | 
				
			||||||
 | 
					export * from './forwarder_test_factory';
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user