@0x/contracts-exchange: Add MultiAssetProxy, ERC1155Fungible, and ERC1155NonFungible combinatorial tests.
				
					
				
			This commit is contained in:
		
				
					committed by
					
						
						Amir Bandeali
					
				
			
			
				
	
			
			
			
						parent
						
							741fdfa52e
						
					
				
				
					commit
					85ea291745
				
			@@ -9,7 +9,6 @@ import {
 | 
			
		||||
    ExpirationTimeSecondsScenario,
 | 
			
		||||
    FeeAssetDataScenario,
 | 
			
		||||
    FeeRecipientAddressScenario,
 | 
			
		||||
    FillScenario,
 | 
			
		||||
    OrderAssetAmountScenario,
 | 
			
		||||
    TakerAssetFillAmountScenario,
 | 
			
		||||
    TakerScenario,
 | 
			
		||||
@@ -52,7 +51,7 @@ const defaultFillScenario = {
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
describe('FillOrder Tests', () => {
 | 
			
		||||
describe.only('FillOrder Tests', () => {
 | 
			
		||||
    let fillOrderCombinatorialUtils: FillOrderCombinatorialUtils;
 | 
			
		||||
 | 
			
		||||
    before(async () => {
 | 
			
		||||
@@ -68,26 +67,7 @@ describe('FillOrder Tests', () => {
 | 
			
		||||
    afterEach(async () => {
 | 
			
		||||
        await blockchainLifecycle.revertAsync();
 | 
			
		||||
    });
 | 
			
		||||
    describe('fillOrder', () => {
 | 
			
		||||
        const test = (fillScenarios: FillScenario[]) => {
 | 
			
		||||
            _.forEach(fillScenarios, fillScenario => {
 | 
			
		||||
                const description = `Combinatorial OrderFill: ${JSON.stringify(fillScenario)}`;
 | 
			
		||||
                it(description, async () => {
 | 
			
		||||
                    await fillOrderCombinatorialUtils.testFillOrderScenarioAsync(fillScenario);
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const allFillScenarios = FillOrderCombinatorialUtils.generateFillOrderCombinations();
 | 
			
		||||
        describe('Combinatorially generated fills orders', () => test(allFillScenarios));
 | 
			
		||||
 | 
			
		||||
        it('should transfer the correct amounts when makerAssetAmount === takerAssetAmount', async () => {
 | 
			
		||||
            const fillScenario = {
 | 
			
		||||
                ...defaultFillScenario,
 | 
			
		||||
            };
 | 
			
		||||
            await fillOrderCombinatorialUtils.testFillOrderScenarioSuccessAsync(fillScenario);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
    describe('Fill tests', () => {
 | 
			
		||||
        it('should transfer the correct amounts when makerAssetAmount > takerAssetAmount', async () => {
 | 
			
		||||
            const fillScenario = {
 | 
			
		||||
                ...defaultFillScenario,
 | 
			
		||||
@@ -141,38 +121,6 @@ describe('FillOrder Tests', () => {
 | 
			
		||||
            await fillOrderCombinatorialUtils.testFillOrderScenarioSuccessAsync(fillScenario);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('should be able to pay maker fee with taker asset', async () => {
 | 
			
		||||
            const fillScenario = {
 | 
			
		||||
                ...defaultFillScenario,
 | 
			
		||||
                orderScenario: {
 | 
			
		||||
                    ...defaultFillScenario.orderScenario,
 | 
			
		||||
                    makerFeeAssetDataScenario: FeeAssetDataScenario.TakerToken,
 | 
			
		||||
                },
 | 
			
		||||
                takerAssetFillAmountScenario: TakerAssetFillAmountScenario.ExactlyTakerAssetAmount,
 | 
			
		||||
                makerStateScenario: {
 | 
			
		||||
                    ...defaultFillScenario.makerStateScenario,
 | 
			
		||||
                    feeBalance: BalanceAmountScenario.Zero,
 | 
			
		||||
                },
 | 
			
		||||
            };
 | 
			
		||||
            await fillOrderCombinatorialUtils.testFillOrderScenarioSuccessAsync(fillScenario);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('should be able to pay taker fee with maker asset', async () => {
 | 
			
		||||
            const fillScenario = {
 | 
			
		||||
                ...defaultFillScenario,
 | 
			
		||||
                orderScenario: {
 | 
			
		||||
                    ...defaultFillScenario.orderScenario,
 | 
			
		||||
                    takerFeeAssetDataScenario: FeeAssetDataScenario.MakerToken,
 | 
			
		||||
                },
 | 
			
		||||
                takerAssetFillAmountScenario: TakerAssetFillAmountScenario.ExactlyTakerAssetAmount,
 | 
			
		||||
                takerStateScenario: {
 | 
			
		||||
                    ...defaultFillScenario.takerStateScenario,
 | 
			
		||||
                    feeBalance: BalanceAmountScenario.Zero,
 | 
			
		||||
                },
 | 
			
		||||
            };
 | 
			
		||||
            await fillOrderCombinatorialUtils.testFillOrderScenarioSuccessAsync(fillScenario);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('should throw when taker is specified and order is claimed by other', async () => {
 | 
			
		||||
            const fillScenario = {
 | 
			
		||||
                ...defaultFillScenario,
 | 
			
		||||
@@ -226,8 +174,92 @@ describe('FillOrder Tests', () => {
 | 
			
		||||
            };
 | 
			
		||||
            await fillOrderCombinatorialUtils.testFillOrderScenarioFailureAsync(fillScenario);
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
        it('should throw if maker erc20Balances are too low to fill order', async () => {
 | 
			
		||||
    describe('ERC20', () => {
 | 
			
		||||
        it('should be able to pay maker fee with taker asset', async () => {
 | 
			
		||||
            const fillScenario = {
 | 
			
		||||
                ...defaultFillScenario,
 | 
			
		||||
                orderScenario: {
 | 
			
		||||
                    ...defaultFillScenario.orderScenario,
 | 
			
		||||
                    makerFeeAssetDataScenario: FeeAssetDataScenario.TakerToken,
 | 
			
		||||
                },
 | 
			
		||||
                takerAssetFillAmountScenario: TakerAssetFillAmountScenario.ExactlyTakerAssetAmount,
 | 
			
		||||
                makerStateScenario: {
 | 
			
		||||
                    ...defaultFillScenario.makerStateScenario,
 | 
			
		||||
                    feeBalance: BalanceAmountScenario.Zero,
 | 
			
		||||
                },
 | 
			
		||||
            };
 | 
			
		||||
            await fillOrderCombinatorialUtils.testFillOrderScenarioSuccessAsync(fillScenario);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('should be able to pay taker fee with maker asset', async () => {
 | 
			
		||||
            const fillScenario = {
 | 
			
		||||
                ...defaultFillScenario,
 | 
			
		||||
                orderScenario: {
 | 
			
		||||
                    ...defaultFillScenario.orderScenario,
 | 
			
		||||
                    takerFeeAssetDataScenario: FeeAssetDataScenario.MakerToken,
 | 
			
		||||
                },
 | 
			
		||||
                takerAssetFillAmountScenario: TakerAssetFillAmountScenario.ExactlyTakerAssetAmount,
 | 
			
		||||
                takerStateScenario: {
 | 
			
		||||
                    ...defaultFillScenario.takerStateScenario,
 | 
			
		||||
                    feeBalance: BalanceAmountScenario.Zero,
 | 
			
		||||
                },
 | 
			
		||||
            };
 | 
			
		||||
            await fillOrderCombinatorialUtils.testFillOrderScenarioSuccessAsync(fillScenario);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('should not be able to pay maker fee with maker asset if none is left over (double-spend)', async () => {
 | 
			
		||||
            const fillScenario = {
 | 
			
		||||
                ...defaultFillScenario,
 | 
			
		||||
                orderScenario: {
 | 
			
		||||
                    ...defaultFillScenario.orderScenario,
 | 
			
		||||
                    makerFeeAssetDataScenario: FeeAssetDataScenario.MakerToken,
 | 
			
		||||
                },
 | 
			
		||||
                takerAssetFillAmountScenario: TakerAssetFillAmountScenario.ExactlyTakerAssetAmount,
 | 
			
		||||
                makerStateScenario: {
 | 
			
		||||
                    ...defaultFillScenario.makerStateScenario,
 | 
			
		||||
                    traderAssetBalance: BalanceAmountScenario.Exact,
 | 
			
		||||
                    feeBalance: BalanceAmountScenario.Zero,
 | 
			
		||||
                },
 | 
			
		||||
            };
 | 
			
		||||
            await fillOrderCombinatorialUtils.testFillOrderScenarioFailureAsync(fillScenario);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('should not be able to pay taker fee with taker asset if none is left over (double-spend)', async () => {
 | 
			
		||||
            const fillScenario = {
 | 
			
		||||
                ...defaultFillScenario,
 | 
			
		||||
                orderScenario: {
 | 
			
		||||
                    ...defaultFillScenario.orderScenario,
 | 
			
		||||
                    takerFeeAssetDataScenario: FeeAssetDataScenario.TakerToken,
 | 
			
		||||
                },
 | 
			
		||||
                takerAssetFillAmountScenario: TakerAssetFillAmountScenario.ExactlyTakerAssetAmount,
 | 
			
		||||
                takerStateScenario: {
 | 
			
		||||
                    ...defaultFillScenario.takerStateScenario,
 | 
			
		||||
                    traderAssetBalance: BalanceAmountScenario.Exact,
 | 
			
		||||
                    feeBalance: BalanceAmountScenario.Zero,
 | 
			
		||||
                },
 | 
			
		||||
            };
 | 
			
		||||
            await fillOrderCombinatorialUtils.testFillOrderScenarioFailureAsync(fillScenario);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('should be able to pay taker fee with maker asset', async () => {
 | 
			
		||||
            const fillScenario = {
 | 
			
		||||
                ...defaultFillScenario,
 | 
			
		||||
                orderScenario: {
 | 
			
		||||
                    ...defaultFillScenario.orderScenario,
 | 
			
		||||
                    takerFeeAssetDataScenario: FeeAssetDataScenario.MakerToken,
 | 
			
		||||
                },
 | 
			
		||||
                takerAssetFillAmountScenario: TakerAssetFillAmountScenario.ExactlyTakerAssetAmount,
 | 
			
		||||
                takerStateScenario: {
 | 
			
		||||
                    ...defaultFillScenario.takerStateScenario,
 | 
			
		||||
                    feeBalance: BalanceAmountScenario.Zero,
 | 
			
		||||
                },
 | 
			
		||||
            };
 | 
			
		||||
            await fillOrderCombinatorialUtils.testFillOrderScenarioSuccessAsync(fillScenario);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('should throw if maker balance is too low to fill order', async () => {
 | 
			
		||||
            const fillScenario = {
 | 
			
		||||
                ...defaultFillScenario,
 | 
			
		||||
                makerStateScenario: {
 | 
			
		||||
@@ -238,7 +270,7 @@ describe('FillOrder Tests', () => {
 | 
			
		||||
            await fillOrderCombinatorialUtils.testFillOrderScenarioFailureAsync(fillScenario);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('should throw if taker erc20Balances are too low to fill order', async () => {
 | 
			
		||||
        it('should throw if taker balance is too low to fill order', async () => {
 | 
			
		||||
            const fillScenario = {
 | 
			
		||||
                ...defaultFillScenario,
 | 
			
		||||
                takerStateScenario: {
 | 
			
		||||
@@ -271,7 +303,7 @@ describe('FillOrder Tests', () => {
 | 
			
		||||
            await fillOrderCombinatorialUtils.testFillOrderScenarioFailureAsync(fillScenario);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('should throw if maker fee erc20Balances are too low to fill order', async () => {
 | 
			
		||||
        it('should throw if maker fee balance is too low to fill order', async () => {
 | 
			
		||||
            const fillScenario = {
 | 
			
		||||
                ...defaultFillScenario,
 | 
			
		||||
                makerStateScenario: {
 | 
			
		||||
@@ -282,7 +314,7 @@ describe('FillOrder Tests', () => {
 | 
			
		||||
            await fillOrderCombinatorialUtils.testFillOrderScenarioFailureAsync(fillScenario);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('should throw if taker fee erc20Balances are too low to fill order', async () => {
 | 
			
		||||
        it('should throw if taker fee balance is too low to fill order', async () => {
 | 
			
		||||
            const fillScenario = {
 | 
			
		||||
                ...defaultFillScenario,
 | 
			
		||||
                takerStateScenario: {
 | 
			
		||||
@@ -316,85 +348,8 @@ describe('FillOrder Tests', () => {
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    describe('Testing exchange of ERC721 Tokens', () => {
 | 
			
		||||
        it('should successfully exchange a single token between the maker and taker (via fillOrder)', async () => {
 | 
			
		||||
            const fillScenario = {
 | 
			
		||||
                ...defaultFillScenario,
 | 
			
		||||
                orderScenario: {
 | 
			
		||||
                    ...defaultFillScenario.orderScenario,
 | 
			
		||||
                    makerAssetDataScenario: AssetDataScenario.ERC721,
 | 
			
		||||
                    takerAssetDataScenario: AssetDataScenario.ERC721,
 | 
			
		||||
                },
 | 
			
		||||
                takerAssetFillAmountScenario: TakerAssetFillAmountScenario.ExactlyTakerAssetAmount,
 | 
			
		||||
            };
 | 
			
		||||
            await fillOrderCombinatorialUtils.testFillOrderScenarioSuccessAsync(fillScenario);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('should successfully fill order when makerAsset is ERC721 and takerAsset is ERC20', async () => {
 | 
			
		||||
            const fillScenario = {
 | 
			
		||||
                ...defaultFillScenario,
 | 
			
		||||
                orderScenario: {
 | 
			
		||||
                    ...defaultFillScenario.orderScenario,
 | 
			
		||||
                    makerAssetDataScenario: AssetDataScenario.ERC721,
 | 
			
		||||
                    takerAssetDataScenario: AssetDataScenario.ERC20EighteenDecimals,
 | 
			
		||||
                },
 | 
			
		||||
                takerAssetFillAmountScenario: TakerAssetFillAmountScenario.ExactlyTakerAssetAmount,
 | 
			
		||||
            };
 | 
			
		||||
            await fillOrderCombinatorialUtils.testFillOrderScenarioSuccessAsync(fillScenario);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('should successfully fill order when makerAsset is ERC20 and takerAsset is ERC721', async () => {
 | 
			
		||||
            const fillScenario = {
 | 
			
		||||
                ...defaultFillScenario,
 | 
			
		||||
                orderScenario: {
 | 
			
		||||
                    ...defaultFillScenario.orderScenario,
 | 
			
		||||
                    makerAssetDataScenario: AssetDataScenario.ERC20EighteenDecimals,
 | 
			
		||||
                    takerAssetDataScenario: AssetDataScenario.ERC721,
 | 
			
		||||
                },
 | 
			
		||||
                takerAssetFillAmountScenario: TakerAssetFillAmountScenario.ExactlyTakerAssetAmount,
 | 
			
		||||
            };
 | 
			
		||||
            await fillOrderCombinatorialUtils.testFillOrderScenarioSuccessAsync(fillScenario);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('should successfully fill order when makerAsset is ERC721 and approveAll is set for it', async () => {
 | 
			
		||||
            const fillScenario = {
 | 
			
		||||
                ...defaultFillScenario,
 | 
			
		||||
                orderScenario: {
 | 
			
		||||
                    ...defaultFillScenario.orderScenario,
 | 
			
		||||
                    makerAssetDataScenario: AssetDataScenario.ERC721,
 | 
			
		||||
                    takerAssetDataScenario: AssetDataScenario.ERC20EighteenDecimals,
 | 
			
		||||
                },
 | 
			
		||||
                takerAssetFillAmountScenario: TakerAssetFillAmountScenario.ExactlyTakerAssetAmount,
 | 
			
		||||
                makerStateScenario: {
 | 
			
		||||
                    ...defaultFillScenario.makerStateScenario,
 | 
			
		||||
                    traderAssetAllowance: AllowanceAmountScenario.Unlimited,
 | 
			
		||||
                },
 | 
			
		||||
            };
 | 
			
		||||
            await fillOrderCombinatorialUtils.testFillOrderScenarioSuccessAsync(fillScenario);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('should successfully fill order when makerAsset and takerAsset are ERC721 and approveAll is set for them', async () => {
 | 
			
		||||
            const fillScenario = {
 | 
			
		||||
                ...defaultFillScenario,
 | 
			
		||||
                orderScenario: {
 | 
			
		||||
                    ...defaultFillScenario.orderScenario,
 | 
			
		||||
                    makerAssetDataScenario: AssetDataScenario.ERC721,
 | 
			
		||||
                    takerAssetDataScenario: AssetDataScenario.ERC721,
 | 
			
		||||
                },
 | 
			
		||||
                takerAssetFillAmountScenario: TakerAssetFillAmountScenario.ExactlyTakerAssetAmount,
 | 
			
		||||
                makerStateScenario: {
 | 
			
		||||
                    ...defaultFillScenario.makerStateScenario,
 | 
			
		||||
                    traderAssetAllowance: AllowanceAmountScenario.Unlimited,
 | 
			
		||||
                },
 | 
			
		||||
                takerStateScenario: {
 | 
			
		||||
                    ...defaultFillScenario.takerStateScenario,
 | 
			
		||||
                    traderAssetAllowance: AllowanceAmountScenario.Unlimited,
 | 
			
		||||
                },
 | 
			
		||||
            };
 | 
			
		||||
            await fillOrderCombinatorialUtils.testFillOrderScenarioSuccessAsync(fillScenario);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('should be able to pay maker fee with taker asset', async () => {
 | 
			
		||||
    describe('ERC721', () => {
 | 
			
		||||
        it('should be able to pay maker fee with taker ERC721', async () => {
 | 
			
		||||
            const fillScenario = {
 | 
			
		||||
                ...defaultFillScenario,
 | 
			
		||||
                orderScenario: {
 | 
			
		||||
@@ -406,13 +361,12 @@ describe('FillOrder Tests', () => {
 | 
			
		||||
                makerStateScenario: {
 | 
			
		||||
                    ...defaultFillScenario.makerStateScenario,
 | 
			
		||||
                    feeBalance: BalanceAmountScenario.Zero,
 | 
			
		||||
                    feeAllowance: AllowanceAmountScenario.Unlimited,
 | 
			
		||||
                },
 | 
			
		||||
            };
 | 
			
		||||
            await fillOrderCombinatorialUtils.testFillOrderScenarioSuccessAsync(fillScenario);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('should be able to pay taker fee with maker asset', async () => {
 | 
			
		||||
        it('should be able to pay taker fee with maker ERC721', async () => {
 | 
			
		||||
            const fillScenario = {
 | 
			
		||||
                ...defaultFillScenario,
 | 
			
		||||
                orderScenario: {
 | 
			
		||||
@@ -424,10 +378,259 @@ describe('FillOrder Tests', () => {
 | 
			
		||||
                takerStateScenario: {
 | 
			
		||||
                    ...defaultFillScenario.takerStateScenario,
 | 
			
		||||
                    feeBalance: BalanceAmountScenario.Zero,
 | 
			
		||||
                    feeAllowance: AllowanceAmountScenario.Unlimited,
 | 
			
		||||
                },
 | 
			
		||||
            };
 | 
			
		||||
            await fillOrderCombinatorialUtils.testFillOrderScenarioSuccessAsync(fillScenario);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('should not be able to pay maker fee with maker ERC721 (double-spend)', async () => {
 | 
			
		||||
            const fillScenario = {
 | 
			
		||||
                ...defaultFillScenario,
 | 
			
		||||
                orderScenario: {
 | 
			
		||||
                    ...defaultFillScenario.orderScenario,
 | 
			
		||||
                    makerAssetDataScenario: AssetDataScenario.ERC721,
 | 
			
		||||
                    makerFeeAssetDataScenario: FeeAssetDataScenario.MakerToken,
 | 
			
		||||
                },
 | 
			
		||||
                takerAssetFillAmountScenario: TakerAssetFillAmountScenario.ExactlyTakerAssetAmount,
 | 
			
		||||
                makerStateScenario: {
 | 
			
		||||
                    ...defaultFillScenario.makerStateScenario,
 | 
			
		||||
                    feeBalance: BalanceAmountScenario.Zero,
 | 
			
		||||
                },
 | 
			
		||||
            };
 | 
			
		||||
            await fillOrderCombinatorialUtils.testFillOrderScenarioFailureAsync(fillScenario);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('should be able to pay taker fee with taker ERC721 (double-spend)', async () => {
 | 
			
		||||
            const fillScenario = {
 | 
			
		||||
                ...defaultFillScenario,
 | 
			
		||||
                orderScenario: {
 | 
			
		||||
                    ...defaultFillScenario.orderScenario,
 | 
			
		||||
                    takerAssetDataScenario: AssetDataScenario.ERC721,
 | 
			
		||||
                    takerFeeAssetDataScenario: FeeAssetDataScenario.TakerToken,
 | 
			
		||||
                },
 | 
			
		||||
                takerAssetFillAmountScenario: TakerAssetFillAmountScenario.ExactlyTakerAssetAmount,
 | 
			
		||||
                takerStateScenario: {
 | 
			
		||||
                    ...defaultFillScenario.takerStateScenario,
 | 
			
		||||
                    feeBalance: BalanceAmountScenario.Zero,
 | 
			
		||||
                },
 | 
			
		||||
            };
 | 
			
		||||
            await fillOrderCombinatorialUtils.testFillOrderScenarioFailureAsync(fillScenario);
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    describe('ERC1155', () => {
 | 
			
		||||
        const assetTypes = [AssetDataScenario.ERC1155Fungible, AssetDataScenario.ERC1155NonFungible];
 | 
			
		||||
        for (const assetType of assetTypes) {
 | 
			
		||||
            describe(_.startCase(_.toLower((/ERC1155(.+)/.exec(assetType) as string[])[1])), () => {
 | 
			
		||||
                it('should be able to pay maker fee with taker asset', async () => {
 | 
			
		||||
                    const fillScenario = {
 | 
			
		||||
                        ...defaultFillScenario,
 | 
			
		||||
                        orderScenario: {
 | 
			
		||||
                            ...defaultFillScenario.orderScenario,
 | 
			
		||||
                            takerAssetDataScenario: assetType,
 | 
			
		||||
                            makerFeeAssetDataScenario: FeeAssetDataScenario.TakerToken,
 | 
			
		||||
                        },
 | 
			
		||||
                        takerAssetFillAmountScenario: TakerAssetFillAmountScenario.ExactlyTakerAssetAmount,
 | 
			
		||||
                        makerStateScenario: {
 | 
			
		||||
                            ...defaultFillScenario.makerStateScenario,
 | 
			
		||||
                            feeBalance: BalanceAmountScenario.Zero,
 | 
			
		||||
                        },
 | 
			
		||||
                    };
 | 
			
		||||
                    await fillOrderCombinatorialUtils.testFillOrderScenarioSuccessAsync(fillScenario);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it('should be able to pay taker fee with maker asset', async () => {
 | 
			
		||||
                    const fillScenario = {
 | 
			
		||||
                        ...defaultFillScenario,
 | 
			
		||||
                        orderScenario: {
 | 
			
		||||
                            ...defaultFillScenario.orderScenario,
 | 
			
		||||
                            makerAssetDataScenario: assetType,
 | 
			
		||||
                            takerFeeAssetDataScenario: FeeAssetDataScenario.MakerToken,
 | 
			
		||||
                        },
 | 
			
		||||
                        takerAssetFillAmountScenario: TakerAssetFillAmountScenario.ExactlyTakerAssetAmount,
 | 
			
		||||
                        takerStateScenario: {
 | 
			
		||||
                            ...defaultFillScenario.takerStateScenario,
 | 
			
		||||
                            feeBalance: BalanceAmountScenario.Zero,
 | 
			
		||||
                        },
 | 
			
		||||
                    };
 | 
			
		||||
                    await fillOrderCombinatorialUtils.testFillOrderScenarioSuccessAsync(fillScenario);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it('should not be able to pay maker fee with maker asset if not enough left (double-spend)', async () => {
 | 
			
		||||
                    const fillScenario = {
 | 
			
		||||
                        ...defaultFillScenario,
 | 
			
		||||
                        orderScenario: {
 | 
			
		||||
                            ...defaultFillScenario.orderScenario,
 | 
			
		||||
                            makerAssetDataScenario: assetType,
 | 
			
		||||
                            makerFeeAssetDataScenario: FeeAssetDataScenario.MakerToken,
 | 
			
		||||
                        },
 | 
			
		||||
                        takerAssetFillAmountScenario: TakerAssetFillAmountScenario.ExactlyTakerAssetAmount,
 | 
			
		||||
                        makerStateScenario: {
 | 
			
		||||
                            ...defaultFillScenario.makerStateScenario,
 | 
			
		||||
                            traderAssetBalance: BalanceAmountScenario.Exact,
 | 
			
		||||
                            feeBalance: BalanceAmountScenario.Zero,
 | 
			
		||||
                        },
 | 
			
		||||
                    };
 | 
			
		||||
                    await fillOrderCombinatorialUtils.testFillOrderScenarioFailureAsync(fillScenario);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it('should be able to pay taker fee with taker asset if not enough left (double-spend)', async () => {
 | 
			
		||||
                    const fillScenario = {
 | 
			
		||||
                        ...defaultFillScenario,
 | 
			
		||||
                        orderScenario: {
 | 
			
		||||
                            ...defaultFillScenario.orderScenario,
 | 
			
		||||
                            takerAssetDataScenario: assetType,
 | 
			
		||||
                            takerFeeAssetDataScenario: FeeAssetDataScenario.TakerToken,
 | 
			
		||||
                        },
 | 
			
		||||
                        takerAssetFillAmountScenario: TakerAssetFillAmountScenario.ExactlyTakerAssetAmount,
 | 
			
		||||
                        takerStateScenario: {
 | 
			
		||||
                            ...defaultFillScenario.takerStateScenario,
 | 
			
		||||
                            traderAssetBalance: BalanceAmountScenario.Exact,
 | 
			
		||||
                            feeBalance: BalanceAmountScenario.Zero,
 | 
			
		||||
                        },
 | 
			
		||||
                    };
 | 
			
		||||
                    await fillOrderCombinatorialUtils.testFillOrderScenarioFailureAsync(fillScenario);
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    describe('MultiAssetProxy', () => {
 | 
			
		||||
        it('should be able to pay maker fee with taker MAP', async () => {
 | 
			
		||||
            const fillScenario = {
 | 
			
		||||
                ...defaultFillScenario,
 | 
			
		||||
                orderScenario: {
 | 
			
		||||
                    ...defaultFillScenario.orderScenario,
 | 
			
		||||
                    takerAssetDataScenario: AssetDataScenario.MultiAssetERC20,
 | 
			
		||||
                    makerFeeAssetDataScenario: FeeAssetDataScenario.TakerToken,
 | 
			
		||||
                },
 | 
			
		||||
                takerAssetFillAmountScenario: TakerAssetFillAmountScenario.ExactlyTakerAssetAmount,
 | 
			
		||||
                makerStateScenario: {
 | 
			
		||||
                    ...defaultFillScenario.makerStateScenario,
 | 
			
		||||
                    feeBalance: BalanceAmountScenario.Zero,
 | 
			
		||||
                },
 | 
			
		||||
            };
 | 
			
		||||
            await fillOrderCombinatorialUtils.testFillOrderScenarioSuccessAsync(fillScenario);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('should be able to pay taker fee with maker MAP', async () => {
 | 
			
		||||
            const fillScenario = {
 | 
			
		||||
                ...defaultFillScenario,
 | 
			
		||||
                orderScenario: {
 | 
			
		||||
                    ...defaultFillScenario.orderScenario,
 | 
			
		||||
                    makerAssetDataScenario: AssetDataScenario.MultiAssetERC20,
 | 
			
		||||
                    takerFeeAssetDataScenario: FeeAssetDataScenario.MakerToken,
 | 
			
		||||
                },
 | 
			
		||||
                takerAssetFillAmountScenario: TakerAssetFillAmountScenario.ExactlyTakerAssetAmount,
 | 
			
		||||
                takerStateScenario: {
 | 
			
		||||
                    ...defaultFillScenario.takerStateScenario,
 | 
			
		||||
                    feeBalance: BalanceAmountScenario.Zero,
 | 
			
		||||
                },
 | 
			
		||||
            };
 | 
			
		||||
            await fillOrderCombinatorialUtils.testFillOrderScenarioSuccessAsync(fillScenario);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('should not be able to pay maker fee with maker MAP (double-spend)', async () => {
 | 
			
		||||
            const fillScenario = {
 | 
			
		||||
                ...defaultFillScenario,
 | 
			
		||||
                orderScenario: {
 | 
			
		||||
                    ...defaultFillScenario.orderScenario,
 | 
			
		||||
                    makerAssetDataScenario: AssetDataScenario.MultiAssetERC20,
 | 
			
		||||
                    makerFeeAssetDataScenario: FeeAssetDataScenario.MakerToken,
 | 
			
		||||
                },
 | 
			
		||||
                takerAssetFillAmountScenario: TakerAssetFillAmountScenario.ExactlyTakerAssetAmount,
 | 
			
		||||
                makerStateScenario: {
 | 
			
		||||
                    ...defaultFillScenario.makerStateScenario,
 | 
			
		||||
                    feeBalance: BalanceAmountScenario.Zero,
 | 
			
		||||
                },
 | 
			
		||||
            };
 | 
			
		||||
            await fillOrderCombinatorialUtils.testFillOrderScenarioFailureAsync(fillScenario);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('should be able to pay taker fee with taker MAP (double-spend)', async () => {
 | 
			
		||||
            const fillScenario = {
 | 
			
		||||
                ...defaultFillScenario,
 | 
			
		||||
                orderScenario: {
 | 
			
		||||
                    ...defaultFillScenario.orderScenario,
 | 
			
		||||
                    takerAssetDataScenario: AssetDataScenario.MultiAssetERC20,
 | 
			
		||||
                    takerFeeAssetDataScenario: FeeAssetDataScenario.TakerToken,
 | 
			
		||||
                },
 | 
			
		||||
                takerAssetFillAmountScenario: TakerAssetFillAmountScenario.ExactlyTakerAssetAmount,
 | 
			
		||||
                takerStateScenario: {
 | 
			
		||||
                    ...defaultFillScenario.takerStateScenario,
 | 
			
		||||
                    feeBalance: BalanceAmountScenario.Zero,
 | 
			
		||||
                },
 | 
			
		||||
            };
 | 
			
		||||
            await fillOrderCombinatorialUtils.testFillOrderScenarioFailureAsync(fillScenario);
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    describe('Maker/taker asset combinations', () => {
 | 
			
		||||
        const assetDataScenarios = [
 | 
			
		||||
            AssetDataScenario.ERC20EighteenDecimals,
 | 
			
		||||
            AssetDataScenario.ERC721,
 | 
			
		||||
            AssetDataScenario.ERC1155Fungible,
 | 
			
		||||
            AssetDataScenario.ERC1155NonFungible,
 | 
			
		||||
            AssetDataScenario.MultiAssetERC20,
 | 
			
		||||
        ];
 | 
			
		||||
        for (const [makerAssetData, takerAssetData] of getAllPossiblePairs(assetDataScenarios)) {
 | 
			
		||||
            it(`should successfully exchange ${makerAssetData} for ${takerAssetData}`, async () => {
 | 
			
		||||
                const fillScenario = {
 | 
			
		||||
                    ...defaultFillScenario,
 | 
			
		||||
                    orderScenario: {
 | 
			
		||||
                        ...defaultFillScenario.orderScenario,
 | 
			
		||||
                        makerAssetDataScenario: makerAssetData,
 | 
			
		||||
                        takerAssetDataScenario: takerAssetData,
 | 
			
		||||
                    },
 | 
			
		||||
                    takerAssetFillAmountScenario: TakerAssetFillAmountScenario.ExactlyTakerAssetAmount,
 | 
			
		||||
                };
 | 
			
		||||
                await fillOrderCombinatorialUtils.testFillOrderScenarioSuccessAsync(fillScenario);
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    describe('Maker/taker fee asset combinations', () => {
 | 
			
		||||
        const feeAssetDataScenarios = [
 | 
			
		||||
            FeeAssetDataScenario.ERC20EighteenDecimals,
 | 
			
		||||
            FeeAssetDataScenario.ERC721,
 | 
			
		||||
            FeeAssetDataScenario.ERC1155Fungible,
 | 
			
		||||
            FeeAssetDataScenario.ERC1155NonFungible,
 | 
			
		||||
            FeeAssetDataScenario.MultiAssetERC20,
 | 
			
		||||
        ];
 | 
			
		||||
        for (const [makerFeeAssetData, takerFeeAssetData] of getAllPossiblePairs(feeAssetDataScenarios)) {
 | 
			
		||||
            it(`should successfully pay maker fee ${makerFeeAssetData} and taker fee ${takerFeeAssetData}`, async () => {
 | 
			
		||||
                const fillScenario = {
 | 
			
		||||
                    ...defaultFillScenario,
 | 
			
		||||
                    orderScenario: {
 | 
			
		||||
                        ...defaultFillScenario.orderScenario,
 | 
			
		||||
                        makerFeeAssetDataScenario: makerFeeAssetData,
 | 
			
		||||
                        takerFeeAssetDataScenario: takerFeeAssetData,
 | 
			
		||||
                    },
 | 
			
		||||
                    takerAssetFillAmountScenario: TakerAssetFillAmountScenario.ExactlyTakerAssetAmount,
 | 
			
		||||
                };
 | 
			
		||||
                await fillOrderCombinatorialUtils.testFillOrderScenarioSuccessAsync(fillScenario);
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    describe('Combinatorially generated fills orders', () => {
 | 
			
		||||
        const allFillScenarios = FillOrderCombinatorialUtils.generateFillOrderCombinations();
 | 
			
		||||
        for (const fillScenario of allFillScenarios) {
 | 
			
		||||
            const description = `Combinatorial OrderFill: ${JSON.stringify(fillScenario)}`;
 | 
			
		||||
            it(description, async () => {
 | 
			
		||||
                await fillOrderCombinatorialUtils.testFillOrderScenarioAsync(fillScenario);
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
function getAllPossiblePairs<T>(choices: T[]): Array<[T, T]> {
 | 
			
		||||
    const pairs: Array<[T, T]> = [];
 | 
			
		||||
    for (const i of _.times(choices.length)) {
 | 
			
		||||
        for (const j of _.times(choices.length)) {
 | 
			
		||||
            pairs.push([choices[i], choices[j]]);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return pairs;
 | 
			
		||||
}
 | 
			
		||||
// tslint:disable: max-file-line-count
 | 
			
		||||
 
 | 
			
		||||
@@ -4,20 +4,25 @@ import { AssetProxyId } from '@0x/types';
 | 
			
		||||
import { BigNumber, errorUtils } from '@0x/utils';
 | 
			
		||||
import * as _ from 'lodash';
 | 
			
		||||
 | 
			
		||||
import { ERC20Wrapper, ERC721Wrapper } from '@0x/contracts-asset-proxy';
 | 
			
		||||
import { ERC1155ProxyWrapper, ERC20Wrapper, ERC721Wrapper } from '@0x/contracts-asset-proxy';
 | 
			
		||||
 | 
			
		||||
interface ProxyIdToAssetWrappers {
 | 
			
		||||
    [proxyId: string]: AbstractAssetWrapper;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const ONE_NFT_UNIT = new BigNumber(1);
 | 
			
		||||
const ZERO_NFT_UNIT = new BigNumber(0);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This class abstracts away the differences between ERC20 and ERC721 tokens so that
 | 
			
		||||
 * the logic that uses it does not need to care what standard a token belongs to.
 | 
			
		||||
 */
 | 
			
		||||
export class AssetWrapper {
 | 
			
		||||
    private readonly _proxyIdToAssetWrappers: ProxyIdToAssetWrappers;
 | 
			
		||||
    constructor(assetWrappers: AbstractAssetWrapper[]) {
 | 
			
		||||
    private readonly _burnerAddress: string;
 | 
			
		||||
    constructor(assetWrappers: AbstractAssetWrapper[], burnerAddress: string) {
 | 
			
		||||
        this._proxyIdToAssetWrappers = {};
 | 
			
		||||
        this._burnerAddress = burnerAddress;
 | 
			
		||||
        _.each(assetWrappers, assetWrapper => {
 | 
			
		||||
            const proxyId = assetWrapper.getProxyId();
 | 
			
		||||
            this._proxyIdToAssetWrappers[proxyId] = assetWrapper;
 | 
			
		||||
@@ -41,9 +46,31 @@ export class AssetWrapper {
 | 
			
		||||
                    assetProxyData.tokenAddress,
 | 
			
		||||
                    assetProxyData.tokenId,
 | 
			
		||||
                );
 | 
			
		||||
                const balance = isOwner ? new BigNumber(1) : new BigNumber(0);
 | 
			
		||||
                const balance = isOwner ? ONE_NFT_UNIT : ZERO_NFT_UNIT;
 | 
			
		||||
                return balance;
 | 
			
		||||
            }
 | 
			
		||||
            case AssetProxyId.ERC1155: {
 | 
			
		||||
                // tslint:disable-next-line:no-unnecessary-type-assertion
 | 
			
		||||
                const assetProxyWrapper = this._proxyIdToAssetWrappers[proxyId] as ERC1155ProxyWrapper;
 | 
			
		||||
                const assetProxyData = assetDataUtils.decodeERC1155AssetData(assetData);
 | 
			
		||||
                const assetWrapper = assetProxyWrapper.getContractWrapper(assetProxyData.tokenAddress);
 | 
			
		||||
                const balances = await Promise.all(
 | 
			
		||||
                    _.map(assetProxyData.tokenIds).map(tokenId => assetWrapper.getBalanceAsync(userAddress, tokenId)),
 | 
			
		||||
                );
 | 
			
		||||
                return BigNumber.min(...balances);
 | 
			
		||||
            }
 | 
			
		||||
            case AssetProxyId.MultiAsset: {
 | 
			
		||||
                const assetProxyData = assetDataUtils.decodeMultiAssetData(assetData);
 | 
			
		||||
                const nestedBalances = await Promise.all(
 | 
			
		||||
                    assetProxyData.nestedAssetData.map(async nestedAssetData =>
 | 
			
		||||
                        this.getBalanceAsync(userAddress, nestedAssetData),
 | 
			
		||||
                    ),
 | 
			
		||||
                );
 | 
			
		||||
                const scaledBalances = _.zip(assetProxyData.amounts, nestedBalances).map(([amount, balance]) =>
 | 
			
		||||
                    (balance as BigNumber).div(amount as BigNumber).integerValue(BigNumber.ROUND_HALF_UP),
 | 
			
		||||
                );
 | 
			
		||||
                return BigNumber.min(...scaledBalances);
 | 
			
		||||
            }
 | 
			
		||||
            default:
 | 
			
		||||
                throw errorUtils.spawnSwitchErr('proxyId', proxyId);
 | 
			
		||||
        }
 | 
			
		||||
@@ -54,13 +81,14 @@ export class AssetWrapper {
 | 
			
		||||
            case AssetProxyId.ERC20: {
 | 
			
		||||
                // tslint:disable-next-line:no-unnecessary-type-assertion
 | 
			
		||||
                const erc20Wrapper = this._proxyIdToAssetWrappers[proxyId] as ERC20Wrapper;
 | 
			
		||||
                await erc20Wrapper.setBalanceAsync(userAddress, assetData, desiredBalance);
 | 
			
		||||
                await erc20Wrapper.setBalanceAsync(
 | 
			
		||||
                    userAddress,
 | 
			
		||||
                    assetData,
 | 
			
		||||
                    desiredBalance.integerValue(BigNumber.ROUND_DOWN),
 | 
			
		||||
                );
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            case AssetProxyId.ERC721: {
 | 
			
		||||
                if (!desiredBalance.eq(0) && !desiredBalance.eq(1)) {
 | 
			
		||||
                    throw new Error(`Balance for ERC721 token can only be set to 0 or 1. Got: ${desiredBalance}`);
 | 
			
		||||
                }
 | 
			
		||||
                // tslint:disable-next-line:no-unnecessary-type-assertion
 | 
			
		||||
                const erc721Wrapper = this._proxyIdToAssetWrappers[proxyId] as ERC721Wrapper;
 | 
			
		||||
                const assetProxyData = assetDataUtils.decodeERC721AssetData(assetData);
 | 
			
		||||
@@ -68,42 +96,137 @@ export class AssetWrapper {
 | 
			
		||||
                    assetProxyData.tokenAddress,
 | 
			
		||||
                    assetProxyData.tokenId,
 | 
			
		||||
                );
 | 
			
		||||
                if (!doesTokenExist && desiredBalance.eq(1)) {
 | 
			
		||||
                if (!doesTokenExist && desiredBalance.gt(0)) {
 | 
			
		||||
                    await erc721Wrapper.mintAsync(assetProxyData.tokenAddress, assetProxyData.tokenId, userAddress);
 | 
			
		||||
                    return;
 | 
			
		||||
                } else if (!doesTokenExist && desiredBalance.eq(0)) {
 | 
			
		||||
                } else if (!doesTokenExist && desiredBalance.lte(0)) {
 | 
			
		||||
                    return; // noop
 | 
			
		||||
                }
 | 
			
		||||
                const tokenOwner = await erc721Wrapper.ownerOfAsync(
 | 
			
		||||
                    assetProxyData.tokenAddress,
 | 
			
		||||
                    assetProxyData.tokenId,
 | 
			
		||||
                );
 | 
			
		||||
                if (userAddress !== tokenOwner && desiredBalance.eq(1)) {
 | 
			
		||||
                if (userAddress !== tokenOwner && desiredBalance.gt(0)) {
 | 
			
		||||
                    await erc721Wrapper.transferFromAsync(
 | 
			
		||||
                        assetProxyData.tokenAddress,
 | 
			
		||||
                        assetProxyData.tokenId,
 | 
			
		||||
                        tokenOwner,
 | 
			
		||||
                        userAddress,
 | 
			
		||||
                    );
 | 
			
		||||
                } else if (tokenOwner === userAddress && desiredBalance.eq(0)) {
 | 
			
		||||
                    // Transfer token to someone else
 | 
			
		||||
                    const userAddresses = await (erc721Wrapper as any)._web3Wrapper.getAvailableAddressesAsync();
 | 
			
		||||
                    const nonOwner = _.find(userAddresses, a => a !== userAddress);
 | 
			
		||||
                } else if (tokenOwner === userAddress && desiredBalance.lte(0)) {
 | 
			
		||||
                    // Burn token
 | 
			
		||||
                    await erc721Wrapper.transferFromAsync(
 | 
			
		||||
                        assetProxyData.tokenAddress,
 | 
			
		||||
                        assetProxyData.tokenId,
 | 
			
		||||
                        tokenOwner,
 | 
			
		||||
                        nonOwner,
 | 
			
		||||
                        this._burnerAddress,
 | 
			
		||||
                    );
 | 
			
		||||
                    return;
 | 
			
		||||
                } else if (
 | 
			
		||||
                    (userAddress !== tokenOwner && desiredBalance.eq(0)) ||
 | 
			
		||||
                    (tokenOwner === userAddress && desiredBalance.eq(1))
 | 
			
		||||
                    (userAddress !== tokenOwner && desiredBalance.lte(0)) ||
 | 
			
		||||
                    (tokenOwner === userAddress && desiredBalance.gt(0))
 | 
			
		||||
                ) {
 | 
			
		||||
                    return; // noop
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            case AssetProxyId.ERC1155: {
 | 
			
		||||
                // tslint:disable-next-line:no-unnecessary-type-assertion
 | 
			
		||||
                const assetProxyWrapper = this._proxyIdToAssetWrappers[proxyId] as ERC1155ProxyWrapper;
 | 
			
		||||
                const assetProxyData = assetDataUtils.decodeERC1155AssetData(assetData);
 | 
			
		||||
                const assetWrapper = assetProxyWrapper.getContractWrapper(assetProxyData.tokenAddress);
 | 
			
		||||
                const tokenValuesSum = BigNumber.sum(...assetProxyData.tokenValues);
 | 
			
		||||
                let tokenValueRatios = assetProxyData.tokenValues;
 | 
			
		||||
                if (!tokenValuesSum.eq(0)) {
 | 
			
		||||
                    tokenValueRatios = assetProxyData.tokenValues.map(v => v.div(tokenValuesSum));
 | 
			
		||||
                }
 | 
			
		||||
                for (const i of _.times(assetProxyData.tokenIds.length)) {
 | 
			
		||||
                    const tokenId = assetProxyData.tokenIds[i];
 | 
			
		||||
                    const tokenValueRatio = tokenValueRatios[i];
 | 
			
		||||
                    const scaledDesiredBalance = desiredBalance.times(tokenValueRatio);
 | 
			
		||||
                    const isFungible = await assetWrapper.isFungibleItemAsync(tokenId);
 | 
			
		||||
                    if (isFungible) {
 | 
			
		||||
                        // Token is fungible.
 | 
			
		||||
                        const currentBalance = await assetWrapper.getBalanceAsync(userAddress, tokenId);
 | 
			
		||||
                        const difference = scaledDesiredBalance
 | 
			
		||||
                            .minus(currentBalance)
 | 
			
		||||
                            .integerValue(BigNumber.ROUND_DOWN);
 | 
			
		||||
                        if (difference.eq(0)) {
 | 
			
		||||
                            // Just right. Nothing to do.
 | 
			
		||||
                        } else if (difference.lt(0)) {
 | 
			
		||||
                            // Too much. Burn some tokens.
 | 
			
		||||
                            await assetWrapper.safeTransferFromAsync(
 | 
			
		||||
                                userAddress,
 | 
			
		||||
                                this._burnerAddress,
 | 
			
		||||
                                tokenId,
 | 
			
		||||
                                difference.abs(),
 | 
			
		||||
                            );
 | 
			
		||||
                        } else {
 | 
			
		||||
                            // difference.gt(0)
 | 
			
		||||
                            // Too little. Mint some tokens.
 | 
			
		||||
                            await assetWrapper.mintKnownFungibleTokensAsync(tokenId, [userAddress], [difference]);
 | 
			
		||||
                        }
 | 
			
		||||
                    } else {
 | 
			
		||||
                        const nftOwner = await assetWrapper.getOwnerOfAsync(tokenId);
 | 
			
		||||
                        if (scaledDesiredBalance.gt(0)) {
 | 
			
		||||
                            if (nftOwner === userAddress) {
 | 
			
		||||
                                // Nothing to do.
 | 
			
		||||
                            } else if (nftOwner !== constants.NULL_ADDRESS) {
 | 
			
		||||
                                // Transfer from current owner.
 | 
			
		||||
                                await assetWrapper.safeTransferFromAsync(nftOwner, userAddress, tokenId, ONE_NFT_UNIT);
 | 
			
		||||
                            } else {
 | 
			
		||||
                                throw new Error(`Cannot mint new ERC1155 tokens with a specific token ID.`);
 | 
			
		||||
                            }
 | 
			
		||||
                        } else {
 | 
			
		||||
                            if (nftOwner === userAddress) {
 | 
			
		||||
                                // Burn the token.
 | 
			
		||||
                                await assetWrapper.safeTransferFromAsync(
 | 
			
		||||
                                    userAddress,
 | 
			
		||||
                                    this._burnerAddress,
 | 
			
		||||
                                    tokenId,
 | 
			
		||||
                                    ONE_NFT_UNIT,
 | 
			
		||||
                                );
 | 
			
		||||
                            } else {
 | 
			
		||||
                                // Nothing to do.
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            case AssetProxyId.MultiAsset: {
 | 
			
		||||
                const assetProxyData = assetDataUtils.decodeMultiAssetData(assetData);
 | 
			
		||||
                const amountsSum = BigNumber.sum(...assetProxyData.amounts);
 | 
			
		||||
                let assetAmountRatios = assetProxyData.amounts;
 | 
			
		||||
                if (!amountsSum.eq(0)) {
 | 
			
		||||
                    assetAmountRatios = assetProxyData.amounts.map(amt => amt.div(amountsSum));
 | 
			
		||||
                }
 | 
			
		||||
                for (const i of _.times(assetProxyData.amounts.length)) {
 | 
			
		||||
                    const nestedAssetData = assetProxyData.nestedAssetData[i];
 | 
			
		||||
                    const assetAmountRatio = assetAmountRatios[i];
 | 
			
		||||
                    await this.setBalanceAsync(userAddress, nestedAssetData, desiredBalance.times(assetAmountRatio));
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            default:
 | 
			
		||||
                throw errorUtils.spawnSwitchErr('proxyId', proxyId);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    public async setUnscaledBalanceAsync(
 | 
			
		||||
        userAddress: string,
 | 
			
		||||
        assetData: string,
 | 
			
		||||
        desiredBalance: BigNumber,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        const proxyId = assetDataUtils.decodeAssetProxyId(assetData);
 | 
			
		||||
        switch (proxyId) {
 | 
			
		||||
            case AssetProxyId.ERC20:
 | 
			
		||||
            case AssetProxyId.ERC721:
 | 
			
		||||
                return this.setBalanceAsync(userAddress, assetData, desiredBalance);
 | 
			
		||||
            case AssetProxyId.ERC1155:
 | 
			
		||||
            case AssetProxyId.MultiAsset: {
 | 
			
		||||
                const currentBalance = await this.getBalanceAsync(userAddress, assetData);
 | 
			
		||||
                return this.setBalanceAsync(userAddress, assetData, desiredBalance.times(currentBalance));
 | 
			
		||||
            }
 | 
			
		||||
            default:
 | 
			
		||||
                throw errorUtils.spawnSwitchErr('proxyId', proxyId);
 | 
			
		||||
        }
 | 
			
		||||
@@ -133,9 +256,32 @@ export class AssetWrapper {
 | 
			
		||||
                    erc721ProxyData.tokenAddress,
 | 
			
		||||
                    erc721ProxyData.tokenId,
 | 
			
		||||
                );
 | 
			
		||||
                const allowance = isProxyApproved ? new BigNumber(1) : new BigNumber(0);
 | 
			
		||||
                const allowance = isProxyApproved ? ONE_NFT_UNIT : ZERO_NFT_UNIT;
 | 
			
		||||
                return allowance;
 | 
			
		||||
            }
 | 
			
		||||
            case AssetProxyId.ERC1155: {
 | 
			
		||||
                // tslint:disable-next-line:no-unnecessary-type-assertion
 | 
			
		||||
                const assetProxyWrapper = this._proxyIdToAssetWrappers[proxyId] as ERC1155ProxyWrapper;
 | 
			
		||||
                const assetProxyData = assetDataUtils.decodeERC1155AssetData(assetData);
 | 
			
		||||
                const isApprovedForAll = await assetProxyWrapper.isProxyApprovedForAllAsync(
 | 
			
		||||
                    userAddress,
 | 
			
		||||
                    assetProxyData.tokenAddress,
 | 
			
		||||
                );
 | 
			
		||||
                if (!isApprovedForAll) {
 | 
			
		||||
                    // ERC1155 is all or nothing.
 | 
			
		||||
                    return constants.ZERO_AMOUNT;
 | 
			
		||||
                }
 | 
			
		||||
                return constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS;
 | 
			
		||||
            }
 | 
			
		||||
            case AssetProxyId.MultiAsset: {
 | 
			
		||||
                const assetProxyData = assetDataUtils.decodeMultiAssetData(assetData);
 | 
			
		||||
                const allowances = await Promise.all(
 | 
			
		||||
                    assetProxyData.nestedAssetData.map(async nestedAssetData =>
 | 
			
		||||
                        this.getProxyAllowanceAsync(userAddress, nestedAssetData),
 | 
			
		||||
                    ),
 | 
			
		||||
                );
 | 
			
		||||
                return BigNumber.min(...allowances);
 | 
			
		||||
            }
 | 
			
		||||
            default:
 | 
			
		||||
                throw errorUtils.spawnSwitchErr('proxyId', proxyId);
 | 
			
		||||
        }
 | 
			
		||||
@@ -209,9 +355,37 @@ export class AssetWrapper {
 | 
			
		||||
                    (!isProxyApproved && desiredAllowance.eq(0)) ||
 | 
			
		||||
                    (isProxyApproved && desiredAllowance.eq(1))
 | 
			
		||||
                ) {
 | 
			
		||||
                    return; // noop
 | 
			
		||||
                    // noop
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            case AssetProxyId.ERC1155: {
 | 
			
		||||
                // tslint:disable-next-line:no-unnecessary-type-assertion
 | 
			
		||||
                const assetProxyWrapper = this._proxyIdToAssetWrappers[proxyId] as ERC1155ProxyWrapper;
 | 
			
		||||
                const assetProxyData = assetDataUtils.decodeERC1155AssetData(assetData);
 | 
			
		||||
                // ERC1155 allowances are all or nothing.
 | 
			
		||||
                const shouldApprovedForAll = desiredAllowance.gt(0);
 | 
			
		||||
                const currentAllowance = await this.getProxyAllowanceAsync(userAddress, assetData);
 | 
			
		||||
                if (shouldApprovedForAll && currentAllowance.eq(constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS)) {
 | 
			
		||||
                    // Nothing to do.
 | 
			
		||||
                } else if (!shouldApprovedForAll && currentAllowance.eq(constants.ZERO_AMOUNT)) {
 | 
			
		||||
                    // Nothing to do.
 | 
			
		||||
                } else {
 | 
			
		||||
                    assetProxyWrapper.setProxyAllowanceForAllAsync(
 | 
			
		||||
                        userAddress,
 | 
			
		||||
                        assetProxyData.tokenAddress,
 | 
			
		||||
                        shouldApprovedForAll,
 | 
			
		||||
                    );
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            case AssetProxyId.MultiAsset: {
 | 
			
		||||
                const assetProxyData = assetDataUtils.decodeMultiAssetData(assetData);
 | 
			
		||||
                await Promise.all(
 | 
			
		||||
                    assetProxyData.nestedAssetData.map(async nestedAssetData =>
 | 
			
		||||
                        this.setProxyAllowanceAsync(userAddress, nestedAssetData, desiredAllowance),
 | 
			
		||||
                    ),
 | 
			
		||||
                );
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            default:
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +1,13 @@
 | 
			
		||||
import { ERC20Wrapper, ERC721Wrapper } from '@0x/contracts-asset-proxy';
 | 
			
		||||
import { chaiSetup, constants, FillResults, orderUtils, signingUtils } from '@0x/contracts-test-utils';
 | 
			
		||||
import {
 | 
			
		||||
    assetDataUtils,
 | 
			
		||||
    BalanceAndProxyAllowanceLazyStore,
 | 
			
		||||
    ExchangeRevertErrors,
 | 
			
		||||
    orderHashUtils,
 | 
			
		||||
} from '@0x/order-utils';
 | 
			
		||||
import { AssetProxyId, Order, SignatureType, SignedOrder } from '@0x/types';
 | 
			
		||||
    artifacts as assetProxyArtifacts,
 | 
			
		||||
    ERC1155ProxyWrapper,
 | 
			
		||||
    ERC20Wrapper,
 | 
			
		||||
    ERC721Wrapper,
 | 
			
		||||
    MultiAssetProxyContract,
 | 
			
		||||
} from '@0x/contracts-asset-proxy';
 | 
			
		||||
import { chaiSetup, constants, FillResults, orderUtils, signingUtils } from '@0x/contracts-test-utils';
 | 
			
		||||
import { BalanceAndProxyAllowanceLazyStore, ExchangeRevertErrors, orderHashUtils } from '@0x/order-utils';
 | 
			
		||||
import { Order, SignatureType, SignedOrder } from '@0x/types';
 | 
			
		||||
import { BigNumber, errorUtils, providerUtils, RevertError, StringRevertError } from '@0x/utils';
 | 
			
		||||
import { SupportedProvider, Web3Wrapper } from '@0x/web3-wrapper';
 | 
			
		||||
import * as chai from 'chai';
 | 
			
		||||
@@ -30,12 +31,10 @@ import {
 | 
			
		||||
    OrderScenario,
 | 
			
		||||
    TakerAssetFillAmountScenario,
 | 
			
		||||
    TakerScenario,
 | 
			
		||||
    TraderStateScenario,
 | 
			
		||||
} from './fill_order_scenarios';
 | 
			
		||||
import { FillOrderError, FillOrderSimulator } from './fill_order_simulator';
 | 
			
		||||
import { OrderFactoryFromScenario } from './order_factory_from_scenario';
 | 
			
		||||
import { SimpleAssetBalanceAndProxyAllowanceFetcher } from './simple_asset_balance_and_proxy_allowance_fetcher';
 | 
			
		||||
import { SimpleOrderFilledCancelledFetcher } from './simple_order_filled_cancelled_fetcher';
 | 
			
		||||
 | 
			
		||||
chaiSetup.configure();
 | 
			
		||||
const expect = chai.expect;
 | 
			
		||||
@@ -66,7 +65,7 @@ export async function fillOrderCombinatorialUtilsFactoryAsync(
 | 
			
		||||
): Promise<FillOrderCombinatorialUtils> {
 | 
			
		||||
    const accounts = await web3Wrapper.getAvailableAddressesAsync();
 | 
			
		||||
    const userAddresses = _.slice(accounts, 0, 5);
 | 
			
		||||
    const [ownerAddress, makerAddress, takerAddress] = userAddresses;
 | 
			
		||||
    const [ownerAddress, makerAddress, takerAddress, burnerAddress] = userAddresses;
 | 
			
		||||
    const makerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[userAddresses.indexOf(makerAddress)];
 | 
			
		||||
 | 
			
		||||
    const supportedProvider = web3Wrapper.getProvider();
 | 
			
		||||
@@ -74,6 +73,7 @@ export async function fillOrderCombinatorialUtilsFactoryAsync(
 | 
			
		||||
    const chainId = await providerUtils.getChainIdAsync(provider);
 | 
			
		||||
    const erc20Wrapper = new ERC20Wrapper(provider, userAddresses, ownerAddress);
 | 
			
		||||
    const erc721Wrapper = new ERC721Wrapper(provider, userAddresses, ownerAddress);
 | 
			
		||||
    const erc1155Wrapper = new ERC1155ProxyWrapper(provider, userAddresses, ownerAddress);
 | 
			
		||||
 | 
			
		||||
    const erc20EighteenDecimalTokenCount = 4;
 | 
			
		||||
    const eighteenDecimals = new BigNumber(18);
 | 
			
		||||
@@ -82,12 +82,12 @@ export async function fillOrderCombinatorialUtilsFactoryAsync(
 | 
			
		||||
        eighteenDecimals,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const erc20FiveDecimalTokenCount = 2;
 | 
			
		||||
    const erc20FiveDecimalTokenCount = 4;
 | 
			
		||||
    const fiveDecimals = new BigNumber(5);
 | 
			
		||||
    const erc20FiveDecimalTokens = await erc20Wrapper.deployDummyTokensAsync(erc20FiveDecimalTokenCount, fiveDecimals);
 | 
			
		||||
 | 
			
		||||
    const erc20ZeroDecimalTokenCount = 4;
 | 
			
		||||
    const zeroDecimals = new BigNumber(0);
 | 
			
		||||
    const erc20ZeroDecimalTokenCount = 2;
 | 
			
		||||
    const erc20ZeroDecimalTokens = await erc20Wrapper.deployDummyTokensAsync(erc20ZeroDecimalTokenCount, zeroDecimals);
 | 
			
		||||
    const erc20Proxy = await erc20Wrapper.deployProxyAsync();
 | 
			
		||||
    await erc20Wrapper.setBalancesAndAllowancesAsync();
 | 
			
		||||
@@ -97,7 +97,18 @@ export async function fillOrderCombinatorialUtilsFactoryAsync(
 | 
			
		||||
    await erc721Wrapper.setBalancesAndAllowancesAsync();
 | 
			
		||||
    const erc721Balances = await erc721Wrapper.getBalancesAsync();
 | 
			
		||||
 | 
			
		||||
    const assetWrapper = new AssetWrapper([erc20Wrapper, erc721Wrapper]);
 | 
			
		||||
    const [erc1155Token] = (await erc1155Wrapper.deployDummyContractsAsync()).map(w => w.getContract());
 | 
			
		||||
    const erc1155Proxy = await erc1155Wrapper.deployProxyAsync();
 | 
			
		||||
    await erc1155Wrapper.setBalancesAndAllowancesAsync();
 | 
			
		||||
    const erc1155Holdings = await erc1155Wrapper.getBalancesAsync();
 | 
			
		||||
 | 
			
		||||
    const multiAssetProxy = await MultiAssetProxyContract.deployFrom0xArtifactAsync(
 | 
			
		||||
        assetProxyArtifacts.MultiAssetProxy,
 | 
			
		||||
        provider,
 | 
			
		||||
        txDefaults,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const assetWrapper = new AssetWrapper([erc20Wrapper, erc721Wrapper, erc1155Wrapper], burnerAddress);
 | 
			
		||||
 | 
			
		||||
    const exchangeContract = await ExchangeContract.deployFrom0xArtifactAsync(
 | 
			
		||||
        artifacts.Exchange,
 | 
			
		||||
@@ -108,13 +119,66 @@ export async function fillOrderCombinatorialUtilsFactoryAsync(
 | 
			
		||||
    const exchangeWrapper = new ExchangeWrapper(exchangeContract, provider);
 | 
			
		||||
    await exchangeWrapper.registerAssetProxyAsync(erc20Proxy.address, ownerAddress);
 | 
			
		||||
    await exchangeWrapper.registerAssetProxyAsync(erc721Proxy.address, ownerAddress);
 | 
			
		||||
    await exchangeWrapper.registerAssetProxyAsync(erc1155Proxy.address, ownerAddress);
 | 
			
		||||
    await exchangeWrapper.registerAssetProxyAsync(multiAssetProxy.address, ownerAddress);
 | 
			
		||||
 | 
			
		||||
    await web3Wrapper.awaitTransactionSuccessAsync(
 | 
			
		||||
        await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchangeContract.address, { from: ownerAddress }),
 | 
			
		||||
    await erc20Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
 | 
			
		||||
        exchangeContract.address,
 | 
			
		||||
        { from: ownerAddress },
 | 
			
		||||
        constants.AWAIT_TRANSACTION_MINED_MS,
 | 
			
		||||
    );
 | 
			
		||||
    await web3Wrapper.awaitTransactionSuccessAsync(
 | 
			
		||||
        await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(exchangeContract.address, { from: ownerAddress }),
 | 
			
		||||
 | 
			
		||||
    await erc721Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
 | 
			
		||||
        exchangeContract.address,
 | 
			
		||||
        { from: ownerAddress },
 | 
			
		||||
        constants.AWAIT_TRANSACTION_MINED_MS,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    await erc1155Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
 | 
			
		||||
        exchangeContract.address,
 | 
			
		||||
        { from: ownerAddress },
 | 
			
		||||
        constants.AWAIT_TRANSACTION_MINED_MS,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    await multiAssetProxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
 | 
			
		||||
        exchangeContract.address,
 | 
			
		||||
        { from: ownerAddress },
 | 
			
		||||
        constants.AWAIT_TRANSACTION_MINED_MS,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    await erc20Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
 | 
			
		||||
        multiAssetProxy.address,
 | 
			
		||||
        { from: ownerAddress },
 | 
			
		||||
        constants.AWAIT_TRANSACTION_MINED_MS,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    await erc721Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
 | 
			
		||||
        multiAssetProxy.address,
 | 
			
		||||
        { from: ownerAddress },
 | 
			
		||||
        constants.AWAIT_TRANSACTION_MINED_MS,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    await erc1155Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
 | 
			
		||||
        multiAssetProxy.address,
 | 
			
		||||
        { from: ownerAddress },
 | 
			
		||||
        constants.AWAIT_TRANSACTION_MINED_MS,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    await multiAssetProxy.registerAssetProxy.awaitTransactionSuccessAsync(
 | 
			
		||||
        erc20Proxy.address,
 | 
			
		||||
        { from: ownerAddress },
 | 
			
		||||
        constants.AWAIT_TRANSACTION_MINED_MS,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    await multiAssetProxy.registerAssetProxy.awaitTransactionSuccessAsync(
 | 
			
		||||
        erc721Proxy.address,
 | 
			
		||||
        { from: ownerAddress },
 | 
			
		||||
        constants.AWAIT_TRANSACTION_MINED_MS,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    await multiAssetProxy.registerAssetProxy.awaitTransactionSuccessAsync(
 | 
			
		||||
        erc1155Proxy.address,
 | 
			
		||||
        { from: ownerAddress },
 | 
			
		||||
        constants.AWAIT_TRANSACTION_MINED_MS,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
@@ -123,8 +187,10 @@ export async function fillOrderCombinatorialUtilsFactoryAsync(
 | 
			
		||||
        erc20EighteenDecimalTokens.map(token => token.address),
 | 
			
		||||
        erc20FiveDecimalTokens.map(token => token.address),
 | 
			
		||||
        erc20ZeroDecimalTokens.map(token => token.address),
 | 
			
		||||
        erc721Token,
 | 
			
		||||
        erc721Token.address,
 | 
			
		||||
        erc1155Token.address,
 | 
			
		||||
        erc721Balances,
 | 
			
		||||
        erc1155Holdings,
 | 
			
		||||
        exchangeContract.address,
 | 
			
		||||
        chainId,
 | 
			
		||||
    );
 | 
			
		||||
@@ -152,7 +218,6 @@ export class FillOrderCombinatorialUtils {
 | 
			
		||||
    public exchangeWrapper: ExchangeWrapper;
 | 
			
		||||
    public assetWrapper: AssetWrapper;
 | 
			
		||||
    public balanceAndProxyAllowanceFetcher: SimpleAssetBalanceAndProxyAllowanceFetcher;
 | 
			
		||||
    public orderFilledCancelledFetcher: SimpleOrderFilledCancelledFetcher;
 | 
			
		||||
 | 
			
		||||
    public static generateFillOrderCombinations(): FillScenario[] {
 | 
			
		||||
        const takerScenarios = [
 | 
			
		||||
@@ -192,16 +257,25 @@ export class FillOrderCombinatorialUtils {
 | 
			
		||||
            AssetDataScenario.ERC20FiveDecimals,
 | 
			
		||||
            AssetDataScenario.ERC20EighteenDecimals,
 | 
			
		||||
            AssetDataScenario.ERC721,
 | 
			
		||||
            AssetDataScenario.ERC1155Fungible,
 | 
			
		||||
            AssetDataScenario.ERC1155NonFungible,
 | 
			
		||||
            AssetDataScenario.MultiAssetERC20,
 | 
			
		||||
        ];
 | 
			
		||||
        const takerAssetDataScenario = [
 | 
			
		||||
            AssetDataScenario.ERC20FiveDecimals,
 | 
			
		||||
            AssetDataScenario.ERC20EighteenDecimals,
 | 
			
		||||
            AssetDataScenario.ERC721,
 | 
			
		||||
            AssetDataScenario.ERC1155Fungible,
 | 
			
		||||
            AssetDataScenario.ERC1155NonFungible,
 | 
			
		||||
            AssetDataScenario.MultiAssetERC20,
 | 
			
		||||
        ];
 | 
			
		||||
        const makerFeeAssetDataScenario = [
 | 
			
		||||
            FeeAssetDataScenario.ERC20FiveDecimals,
 | 
			
		||||
            FeeAssetDataScenario.ERC20EighteenDecimals,
 | 
			
		||||
            FeeAssetDataScenario.ERC721,
 | 
			
		||||
            FeeAssetDataScenario.ERC1155Fungible,
 | 
			
		||||
            FeeAssetDataScenario.ERC1155NonFungible,
 | 
			
		||||
            FeeAssetDataScenario.MultiAssetERC20,
 | 
			
		||||
            FeeAssetDataScenario.MakerToken,
 | 
			
		||||
            FeeAssetDataScenario.TakerToken,
 | 
			
		||||
        ];
 | 
			
		||||
@@ -209,6 +283,9 @@ export class FillOrderCombinatorialUtils {
 | 
			
		||||
            FeeAssetDataScenario.ERC20FiveDecimals,
 | 
			
		||||
            FeeAssetDataScenario.ERC20EighteenDecimals,
 | 
			
		||||
            FeeAssetDataScenario.ERC721,
 | 
			
		||||
            FeeAssetDataScenario.ERC1155Fungible,
 | 
			
		||||
            FeeAssetDataScenario.ERC1155NonFungible,
 | 
			
		||||
            FeeAssetDataScenario.MultiAssetERC20,
 | 
			
		||||
            FeeAssetDataScenario.MakerToken,
 | 
			
		||||
            FeeAssetDataScenario.TakerToken,
 | 
			
		||||
        ];
 | 
			
		||||
@@ -374,7 +451,6 @@ export class FillOrderCombinatorialUtils {
 | 
			
		||||
        this.exchangeWrapper = exchangeWrapper;
 | 
			
		||||
        this.assetWrapper = assetWrapper;
 | 
			
		||||
        this.balanceAndProxyAllowanceFetcher = new SimpleAssetBalanceAndProxyAllowanceFetcher(assetWrapper);
 | 
			
		||||
        this.orderFilledCancelledFetcher = new SimpleOrderFilledCancelledFetcher(exchangeWrapper);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async testFillOrderScenarioAsync(fillScenario: FillScenario): Promise<void> {
 | 
			
		||||
@@ -399,17 +475,9 @@ export class FillOrderCombinatorialUtils {
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        const lazyStore = new BalanceAndProxyAllowanceLazyStore(this.balanceAndProxyAllowanceFetcher);
 | 
			
		||||
        const signedOrder = await this._generateSignedOrder(fillScenario.orderScenario);
 | 
			
		||||
        const takerAssetFillAmount = getTakerAssetFillAmountAsync(
 | 
			
		||||
            signedOrder,
 | 
			
		||||
            fillScenario.takerAssetFillAmountScenario,
 | 
			
		||||
        );
 | 
			
		||||
        const takerAssetFillAmount = getTakerAssetFillAmount(signedOrder, fillScenario);
 | 
			
		||||
 | 
			
		||||
        await this._modifyTraderStateAsync(
 | 
			
		||||
            fillScenario.makerStateScenario,
 | 
			
		||||
            fillScenario.takerStateScenario,
 | 
			
		||||
            signedOrder,
 | 
			
		||||
            takerAssetFillAmount,
 | 
			
		||||
        );
 | 
			
		||||
        await this._modifyTraderStateAsync(fillScenario, signedOrder, takerAssetFillAmount);
 | 
			
		||||
 | 
			
		||||
        let expectedFillResults = EMPTY_FILL_RESULTS;
 | 
			
		||||
        let _fillErrorIfExists = fillErrorIfExists;
 | 
			
		||||
@@ -473,26 +541,38 @@ export class FillOrderCombinatorialUtils {
 | 
			
		||||
        const takerFeeAssetData = signedOrder.takerAssetData;
 | 
			
		||||
        const feeRecipient = signedOrder.feeRecipientAddress;
 | 
			
		||||
 | 
			
		||||
        const expMakerAssetBalanceOfMaker = await lazyStore.getBalanceAsync(makerAssetData, makerAddress);
 | 
			
		||||
        const expMakerAssetAllowanceOfMaker = await lazyStore.getProxyAllowanceAsync(makerAssetData, makerAddress);
 | 
			
		||||
        const expTakerAssetBalanceOfMaker = await lazyStore.getBalanceAsync(takerAssetData, makerAddress);
 | 
			
		||||
        const expMakerFeeAssetBalanceOfMaker = await lazyStore.getBalanceAsync(makerFeeAssetData, makerAddress);
 | 
			
		||||
        const expTakerFeeAssetBalanceOfMaker = await lazyStore.getBalanceAsync(takerFeeAssetData, makerAddress);
 | 
			
		||||
        const expMakerFeeAssetAllowanceOfMaker = await lazyStore.getProxyAllowanceAsync(
 | 
			
		||||
            makerFeeAssetData,
 | 
			
		||||
            makerAddress,
 | 
			
		||||
        );
 | 
			
		||||
        const expTakerAssetBalanceOfTaker = await lazyStore.getBalanceAsync(takerAssetData, this.takerAddress);
 | 
			
		||||
        const expTakerAssetAllowanceOfTaker = await lazyStore.getProxyAllowanceAsync(takerAssetData, this.takerAddress);
 | 
			
		||||
        const expMakerAssetBalanceOfTaker = await lazyStore.getBalanceAsync(makerAssetData, this.takerAddress);
 | 
			
		||||
        const expMakerFeeAssetBalanceOfTaker = await lazyStore.getBalanceAsync(makerFeeAssetData, this.takerAddress);
 | 
			
		||||
        const expTakerFeeAssetBalanceOfTaker = await lazyStore.getBalanceAsync(takerFeeAssetData, this.takerAddress);
 | 
			
		||||
        const expTakerFeeAssetAllowanceOfTaker = await lazyStore.getProxyAllowanceAsync(
 | 
			
		||||
            takerFeeAssetData,
 | 
			
		||||
            this.takerAddress,
 | 
			
		||||
        );
 | 
			
		||||
        const expMakerFeeAssetBalanceOfFeeRecipient = await lazyStore.getBalanceAsync(makerFeeAssetData, feeRecipient);
 | 
			
		||||
        const expTakerFeeAssetBalanceOfFeeRecipient = await lazyStore.getBalanceAsync(takerFeeAssetData, feeRecipient);
 | 
			
		||||
        const [
 | 
			
		||||
            expMakerAssetBalanceOfMaker,
 | 
			
		||||
            expMakerAssetAllowanceOfMaker,
 | 
			
		||||
            expTakerAssetBalanceOfMaker,
 | 
			
		||||
            expMakerFeeAssetBalanceOfMaker,
 | 
			
		||||
            expTakerFeeAssetBalanceOfMaker,
 | 
			
		||||
            expMakerFeeAssetAllowanceOfMaker,
 | 
			
		||||
            expTakerAssetBalanceOfTaker,
 | 
			
		||||
            expTakerAssetAllowanceOfTaker,
 | 
			
		||||
            expMakerAssetBalanceOfTaker,
 | 
			
		||||
            expMakerFeeAssetBalanceOfTaker,
 | 
			
		||||
            expTakerFeeAssetBalanceOfTaker,
 | 
			
		||||
            expTakerFeeAssetAllowanceOfTaker,
 | 
			
		||||
            expMakerFeeAssetBalanceOfFeeRecipient,
 | 
			
		||||
            expTakerFeeAssetBalanceOfFeeRecipient,
 | 
			
		||||
        ] = await Promise.all([
 | 
			
		||||
            lazyStore.getBalanceAsync(makerAssetData, makerAddress),
 | 
			
		||||
            lazyStore.getProxyAllowanceAsync(makerAssetData, makerAddress),
 | 
			
		||||
            lazyStore.getBalanceAsync(takerAssetData, makerAddress),
 | 
			
		||||
            lazyStore.getBalanceAsync(makerFeeAssetData, makerAddress),
 | 
			
		||||
            lazyStore.getBalanceAsync(takerFeeAssetData, makerAddress),
 | 
			
		||||
            lazyStore.getProxyAllowanceAsync(makerFeeAssetData, makerAddress),
 | 
			
		||||
            lazyStore.getBalanceAsync(takerAssetData, this.takerAddress),
 | 
			
		||||
            lazyStore.getProxyAllowanceAsync(takerAssetData, this.takerAddress),
 | 
			
		||||
            lazyStore.getBalanceAsync(makerAssetData, this.takerAddress),
 | 
			
		||||
            lazyStore.getBalanceAsync(makerFeeAssetData, this.takerAddress),
 | 
			
		||||
            lazyStore.getBalanceAsync(takerFeeAssetData, this.takerAddress),
 | 
			
		||||
            lazyStore.getProxyAllowanceAsync(takerFeeAssetData, this.takerAddress),
 | 
			
		||||
            lazyStore.getBalanceAsync(makerFeeAssetData, feeRecipient),
 | 
			
		||||
            lazyStore.getBalanceAsync(takerFeeAssetData, feeRecipient),
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        const expFilledTakerAmount = expectedFillResults.takerAssetFilledAmount;
 | 
			
		||||
        const expFilledMakerAmount = expectedFillResults.makerAssetFilledAmount;
 | 
			
		||||
        const expMakerFeePaid = expectedFillResults.makerFeePaid;
 | 
			
		||||
@@ -517,7 +597,40 @@ export class FillOrderCombinatorialUtils {
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
 | 
			
		||||
        const actFilledTakerAmount = await this.exchangeWrapper.getTakerAssetFilledAmountAsync(orderHash);
 | 
			
		||||
        const [
 | 
			
		||||
            actFilledTakerAmount,
 | 
			
		||||
            actMakerAssetBalanceOfMaker,
 | 
			
		||||
            actMakerAssetAllowanceOfMaker,
 | 
			
		||||
            actTakerAssetBalanceOfMaker,
 | 
			
		||||
            actMakerFeeAssetBalanceOfMaker,
 | 
			
		||||
            actMakerFeeAssetAllowanceOfMaker,
 | 
			
		||||
            actTakerFeeAssetBalanceOfMaker,
 | 
			
		||||
            actTakerAssetBalanceOfTaker,
 | 
			
		||||
            actTakerAssetAllowanceOfTaker,
 | 
			
		||||
            actMakerAssetBalanceOfTaker,
 | 
			
		||||
            actMakerFeeAssetBalanceOfTaker,
 | 
			
		||||
            actTakerFeeAssetBalanceOfTaker,
 | 
			
		||||
            actTakerFeeAssetAllowanceOfTaker,
 | 
			
		||||
            actMakerFeeAssetBalanceOfFeeRecipient,
 | 
			
		||||
            actTakerFeeAssetBalanceOfFeeRecipient,
 | 
			
		||||
        ] = await Promise.all([
 | 
			
		||||
            this.exchangeWrapper.getTakerAssetFilledAmountAsync(orderHash),
 | 
			
		||||
            this.assetWrapper.getBalanceAsync(makerAddress, makerAssetData),
 | 
			
		||||
            this.assetWrapper.getProxyAllowanceAsync(makerAddress, makerAssetData),
 | 
			
		||||
            this.assetWrapper.getBalanceAsync(makerAddress, takerAssetData),
 | 
			
		||||
            this.assetWrapper.getBalanceAsync(makerAddress, makerFeeAssetData),
 | 
			
		||||
            this.assetWrapper.getProxyAllowanceAsync(makerAddress, makerFeeAssetData),
 | 
			
		||||
            this.assetWrapper.getBalanceAsync(makerAddress, takerFeeAssetData),
 | 
			
		||||
            this.assetWrapper.getBalanceAsync(this.takerAddress, takerAssetData),
 | 
			
		||||
            this.assetWrapper.getProxyAllowanceAsync(this.takerAddress, takerAssetData),
 | 
			
		||||
            this.assetWrapper.getBalanceAsync(this.takerAddress, makerAssetData),
 | 
			
		||||
            this.assetWrapper.getBalanceAsync(this.takerAddress, makerFeeAssetData),
 | 
			
		||||
            this.assetWrapper.getBalanceAsync(this.takerAddress, takerFeeAssetData),
 | 
			
		||||
            this.assetWrapper.getProxyAllowanceAsync(this.takerAddress, takerFeeAssetData),
 | 
			
		||||
            this.assetWrapper.getBalanceAsync(feeRecipient, makerFeeAssetData),
 | 
			
		||||
            this.assetWrapper.getBalanceAsync(feeRecipient, takerFeeAssetData),
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        expect(actFilledTakerAmount, 'filledTakerAmount').to.be.bignumber.equal(expFilledTakerAmount);
 | 
			
		||||
 | 
			
		||||
        const exchangeLogs = _.filter(
 | 
			
		||||
@@ -542,105 +655,52 @@ export class FillOrderCombinatorialUtils {
 | 
			
		||||
        expect(log.args.makerAssetData, 'log.args.makerAssetData').to.be.equal(makerAssetData);
 | 
			
		||||
        expect(log.args.takerAssetData, 'log.args.takerAssetData').to.be.equal(takerAssetData);
 | 
			
		||||
 | 
			
		||||
        const actMakerAssetBalanceOfMaker = await this.assetWrapper.getBalanceAsync(makerAddress, makerAssetData);
 | 
			
		||||
        expect(actMakerAssetBalanceOfMaker, 'makerAssetBalanceOfMaker').to.be.bignumber.equal(
 | 
			
		||||
            expMakerAssetBalanceOfMaker,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        const actMakerAssetAllowanceOfMaker = await this.assetWrapper.getProxyAllowanceAsync(
 | 
			
		||||
            makerAddress,
 | 
			
		||||
            makerAssetData,
 | 
			
		||||
        );
 | 
			
		||||
        expect(actMakerAssetAllowanceOfMaker, 'makerAssetAllowanceOfMaker').to.be.bignumber.equal(
 | 
			
		||||
            expMakerAssetAllowanceOfMaker,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        const actTakerAssetBalanceOfMaker = await this.assetWrapper.getBalanceAsync(makerAddress, takerAssetData);
 | 
			
		||||
        expect(actTakerAssetBalanceOfMaker, 'takerAssetBalanceOfMaker').to.be.bignumber.equal(
 | 
			
		||||
            expTakerAssetBalanceOfMaker,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        const actMakerFeeAssetBalanceOfMaker = await this.assetWrapper.getBalanceAsync(makerAddress, makerFeeAssetData);
 | 
			
		||||
        expect(actMakerFeeAssetBalanceOfMaker, 'makerFeeAssetBalanceOfMaker').to.be.bignumber.equal(
 | 
			
		||||
            expMakerFeeAssetBalanceOfMaker,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        const actMakerFeeAssetAllowanceOfMaker = await this.assetWrapper.getProxyAllowanceAsync(
 | 
			
		||||
            makerAddress,
 | 
			
		||||
            makerFeeAssetData,
 | 
			
		||||
        );
 | 
			
		||||
        expect(actMakerFeeAssetAllowanceOfMaker, 'makerFeeAssetAllowanceOfMaker').to.be.bignumber.equal(
 | 
			
		||||
            expMakerFeeAssetAllowanceOfMaker,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        const actTakerFeeAssetBalanceOfMaker = await this.assetWrapper.getBalanceAsync(makerAddress, takerFeeAssetData);
 | 
			
		||||
        expect(actTakerFeeAssetBalanceOfMaker, 'takerFeeAssetBalanceOfMaker').to.be.bignumber.equal(
 | 
			
		||||
            expTakerFeeAssetBalanceOfMaker,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        const actTakerAssetBalanceOfTaker = await this.assetWrapper.getBalanceAsync(this.takerAddress, takerAssetData);
 | 
			
		||||
        expect(actTakerAssetBalanceOfTaker, 'TakerAssetBalanceOfTaker').to.be.bignumber.equal(
 | 
			
		||||
            expTakerAssetBalanceOfTaker,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        const actTakerAssetAllowanceOfTaker = await this.assetWrapper.getProxyAllowanceAsync(
 | 
			
		||||
            this.takerAddress,
 | 
			
		||||
            takerAssetData,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        expect(actTakerAssetAllowanceOfTaker, 'takerAssetAllowanceOfTaker').to.be.bignumber.equal(
 | 
			
		||||
            expTakerAssetAllowanceOfTaker,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        const actMakerAssetBalanceOfTaker = await this.assetWrapper.getBalanceAsync(this.takerAddress, makerAssetData);
 | 
			
		||||
        expect(actMakerAssetBalanceOfTaker, 'makerAssetBalanceOfTaker').to.be.bignumber.equal(
 | 
			
		||||
            expMakerAssetBalanceOfTaker,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        const actMakerFeeAssetBalanceOfTaker = await this.assetWrapper.getBalanceAsync(
 | 
			
		||||
            this.takerAddress,
 | 
			
		||||
            makerFeeAssetData,
 | 
			
		||||
        );
 | 
			
		||||
        expect(actMakerFeeAssetBalanceOfTaker, 'makerFeeAssetBalanceOfTaker').to.be.bignumber.equal(
 | 
			
		||||
            expMakerFeeAssetBalanceOfTaker,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        const actTakerFeeAssetBalanceOfTaker = await this.assetWrapper.getBalanceAsync(
 | 
			
		||||
            this.takerAddress,
 | 
			
		||||
            takerFeeAssetData,
 | 
			
		||||
        );
 | 
			
		||||
        expect(actTakerFeeAssetBalanceOfTaker, 'takerFeeAssetBalanceOfTaker').to.be.bignumber.equal(
 | 
			
		||||
            expTakerFeeAssetBalanceOfTaker,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        const actTakerFeeAssetAllowanceOfTaker = await this.assetWrapper.getProxyAllowanceAsync(
 | 
			
		||||
            this.takerAddress,
 | 
			
		||||
            takerFeeAssetData,
 | 
			
		||||
        );
 | 
			
		||||
        expect(actTakerFeeAssetAllowanceOfTaker, 'takerFeeAssetAllowanceOfTaker').to.be.bignumber.equal(
 | 
			
		||||
            expTakerFeeAssetAllowanceOfTaker,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        const actMakerFeeAssetBalanceOfFeeRecipient = await this.assetWrapper.getBalanceAsync(
 | 
			
		||||
            feeRecipient,
 | 
			
		||||
            makerFeeAssetData,
 | 
			
		||||
        );
 | 
			
		||||
        expect(actMakerFeeAssetBalanceOfFeeRecipient, 'makerFeeAssetBalanceOfFeeRecipient').to.be.bignumber.equal(
 | 
			
		||||
            expMakerFeeAssetBalanceOfFeeRecipient,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        const actTakerFeeAssetBalanceOfFeeRecipient = await this.assetWrapper.getBalanceAsync(
 | 
			
		||||
            feeRecipient,
 | 
			
		||||
            takerFeeAssetData,
 | 
			
		||||
        );
 | 
			
		||||
        expect(actTakerFeeAssetBalanceOfFeeRecipient, 'takerFeeAssetBalanceOfFeeRecipient').to.be.bignumber.equal(
 | 
			
		||||
            expTakerFeeAssetBalanceOfFeeRecipient,
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private async _modifyTraderStateAsync(
 | 
			
		||||
        makerStateScenario: TraderStateScenario,
 | 
			
		||||
        takerStateScenario: TraderStateScenario,
 | 
			
		||||
        fillScenario: FillScenario,
 | 
			
		||||
        signedOrder: SignedOrder,
 | 
			
		||||
        takerAssetFillAmount: BigNumber,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
@@ -663,7 +723,7 @@ export class FillOrderCombinatorialUtils {
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        let makerAssetBalance;
 | 
			
		||||
        switch (makerStateScenario.traderAssetBalance) {
 | 
			
		||||
        switch (fillScenario.makerStateScenario.traderAssetBalance) {
 | 
			
		||||
            case BalanceAmountScenario.Higher:
 | 
			
		||||
                break; // Noop since this is already the default
 | 
			
		||||
 | 
			
		||||
@@ -685,11 +745,11 @@ export class FillOrderCombinatorialUtils {
 | 
			
		||||
            default:
 | 
			
		||||
                throw errorUtils.spawnSwitchErr(
 | 
			
		||||
                    'makerStateScenario.traderAssetBalance',
 | 
			
		||||
                    makerStateScenario.traderAssetBalance,
 | 
			
		||||
                    fillScenario.makerStateScenario.traderAssetBalance,
 | 
			
		||||
                );
 | 
			
		||||
        }
 | 
			
		||||
        if (makerAssetBalance !== undefined) {
 | 
			
		||||
            await this.assetWrapper.setBalanceAsync(
 | 
			
		||||
            await this.assetWrapper.setUnscaledBalanceAsync(
 | 
			
		||||
                signedOrder.makerAddress,
 | 
			
		||||
                signedOrder.makerAssetData,
 | 
			
		||||
                makerAssetBalance,
 | 
			
		||||
@@ -697,7 +757,7 @@ export class FillOrderCombinatorialUtils {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let takerAssetBalance;
 | 
			
		||||
        switch (takerStateScenario.traderAssetBalance) {
 | 
			
		||||
        switch (fillScenario.takerStateScenario.traderAssetBalance) {
 | 
			
		||||
            case BalanceAmountScenario.Higher:
 | 
			
		||||
                break; // Noop since this is already the default
 | 
			
		||||
 | 
			
		||||
@@ -719,15 +779,21 @@ export class FillOrderCombinatorialUtils {
 | 
			
		||||
            default:
 | 
			
		||||
                throw errorUtils.spawnSwitchErr(
 | 
			
		||||
                    'takerStateScenario.traderAssetBalance',
 | 
			
		||||
                    takerStateScenario.traderAssetBalance,
 | 
			
		||||
                    fillScenario.takerStateScenario.traderAssetBalance,
 | 
			
		||||
                );
 | 
			
		||||
        }
 | 
			
		||||
        if (takerAssetBalance !== undefined) {
 | 
			
		||||
            await this.assetWrapper.setBalanceAsync(this.takerAddress, signedOrder.takerAssetData, takerAssetBalance);
 | 
			
		||||
            await this.assetWrapper.setUnscaledBalanceAsync(
 | 
			
		||||
                this.takerAddress,
 | 
			
		||||
                signedOrder.takerAssetData,
 | 
			
		||||
                takerAssetBalance,
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const isMakerFeeAssetMakerAsset =
 | 
			
		||||
            fillScenario.orderScenario.makerFeeAssetDataScenario === FeeAssetDataScenario.MakerToken;
 | 
			
		||||
        let makerFeeBalance;
 | 
			
		||||
        switch (makerStateScenario.feeBalance) {
 | 
			
		||||
        switch (fillScenario.makerStateScenario.feeBalance) {
 | 
			
		||||
            case BalanceAmountScenario.Higher:
 | 
			
		||||
                break; // Noop since this is already the default
 | 
			
		||||
 | 
			
		||||
@@ -747,18 +813,23 @@ export class FillOrderCombinatorialUtils {
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            default:
 | 
			
		||||
                throw errorUtils.spawnSwitchErr('makerStateScenario.feeBalance', makerStateScenario.feeBalance);
 | 
			
		||||
                throw errorUtils.spawnSwitchErr(
 | 
			
		||||
                    'makerStateScenario.feeBalance',
 | 
			
		||||
                    fillScenario.makerStateScenario.feeBalance,
 | 
			
		||||
                );
 | 
			
		||||
        }
 | 
			
		||||
        if (makerFeeBalance !== undefined) {
 | 
			
		||||
            await this.assetWrapper.setBalanceAsync(
 | 
			
		||||
        if (isMakerFeeAssetMakerAsset && makerFeeBalance !== undefined) {
 | 
			
		||||
            await this.assetWrapper.setUnscaledBalanceAsync(
 | 
			
		||||
                signedOrder.makerAddress,
 | 
			
		||||
                signedOrder.makerFeeAssetData,
 | 
			
		||||
                makerFeeBalance,
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const isTakerFeeAssetTakerAsset =
 | 
			
		||||
            fillScenario.orderScenario.takerFeeAssetDataScenario === FeeAssetDataScenario.TakerToken;
 | 
			
		||||
        let takerFeeBalance;
 | 
			
		||||
        switch (takerStateScenario.feeBalance) {
 | 
			
		||||
        switch (fillScenario.takerStateScenario.feeBalance) {
 | 
			
		||||
            case BalanceAmountScenario.Higher:
 | 
			
		||||
                break; // Noop since this is already the default
 | 
			
		||||
 | 
			
		||||
@@ -778,14 +849,21 @@ export class FillOrderCombinatorialUtils {
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            default:
 | 
			
		||||
                throw errorUtils.spawnSwitchErr('takerStateScenario.feeBalance', takerStateScenario.feeBalance);
 | 
			
		||||
                throw errorUtils.spawnSwitchErr(
 | 
			
		||||
                    'takerStateScenario.feeBalance',
 | 
			
		||||
                    fillScenario.takerStateScenario.feeBalance,
 | 
			
		||||
                );
 | 
			
		||||
        }
 | 
			
		||||
        if (takerFeeBalance !== undefined) {
 | 
			
		||||
            await this.assetWrapper.setBalanceAsync(this.takerAddress, signedOrder.takerFeeAssetData, takerFeeBalance);
 | 
			
		||||
        if (isTakerFeeAssetTakerAsset && takerFeeBalance !== undefined) {
 | 
			
		||||
            await this.assetWrapper.setUnscaledBalanceAsync(
 | 
			
		||||
                this.takerAddress,
 | 
			
		||||
                signedOrder.takerFeeAssetData,
 | 
			
		||||
                takerFeeBalance,
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let makerAssetAllowance;
 | 
			
		||||
        switch (makerStateScenario.traderAssetAllowance) {
 | 
			
		||||
        switch (fillScenario.makerStateScenario.traderAssetAllowance) {
 | 
			
		||||
            case AllowanceAmountScenario.Higher:
 | 
			
		||||
                break; // Noop since this is already the default
 | 
			
		||||
 | 
			
		||||
@@ -808,7 +886,7 @@ export class FillOrderCombinatorialUtils {
 | 
			
		||||
            default:
 | 
			
		||||
                throw errorUtils.spawnSwitchErr(
 | 
			
		||||
                    'makerStateScenario.traderAssetAllowance',
 | 
			
		||||
                    makerStateScenario.traderAssetAllowance,
 | 
			
		||||
                    fillScenario.makerStateScenario.traderAssetAllowance,
 | 
			
		||||
                );
 | 
			
		||||
        }
 | 
			
		||||
        if (makerAssetAllowance !== undefined) {
 | 
			
		||||
@@ -820,7 +898,7 @@ export class FillOrderCombinatorialUtils {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let takerAssetAllowance;
 | 
			
		||||
        switch (takerStateScenario.traderAssetAllowance) {
 | 
			
		||||
        switch (fillScenario.takerStateScenario.traderAssetAllowance) {
 | 
			
		||||
            case AllowanceAmountScenario.Higher:
 | 
			
		||||
                break; // Noop since this is already the default
 | 
			
		||||
 | 
			
		||||
@@ -843,7 +921,7 @@ export class FillOrderCombinatorialUtils {
 | 
			
		||||
            default:
 | 
			
		||||
                throw errorUtils.spawnSwitchErr(
 | 
			
		||||
                    'takerStateScenario.traderAssetAllowance',
 | 
			
		||||
                    takerStateScenario.traderAssetAllowance,
 | 
			
		||||
                    fillScenario.takerStateScenario.traderAssetAllowance,
 | 
			
		||||
                );
 | 
			
		||||
        }
 | 
			
		||||
        if (takerAssetAllowance !== undefined) {
 | 
			
		||||
@@ -855,7 +933,7 @@ export class FillOrderCombinatorialUtils {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let makerFeeAllowance;
 | 
			
		||||
        switch (makerStateScenario.feeAllowance) {
 | 
			
		||||
        switch (fillScenario.makerStateScenario.feeAllowance) {
 | 
			
		||||
            case AllowanceAmountScenario.Higher:
 | 
			
		||||
                break; // Noop since this is already the default
 | 
			
		||||
 | 
			
		||||
@@ -876,9 +954,12 @@ export class FillOrderCombinatorialUtils {
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            default:
 | 
			
		||||
                throw errorUtils.spawnSwitchErr('makerStateScenario.feeAllowance', makerStateScenario.feeAllowance);
 | 
			
		||||
                throw errorUtils.spawnSwitchErr(
 | 
			
		||||
                    'makerStateScenario.feeAllowance',
 | 
			
		||||
                    fillScenario.makerStateScenario.feeAllowance,
 | 
			
		||||
                );
 | 
			
		||||
        }
 | 
			
		||||
        if (makerFeeAllowance !== undefined) {
 | 
			
		||||
        if (isMakerFeeAssetMakerAsset && makerFeeAllowance !== undefined) {
 | 
			
		||||
            await this.assetWrapper.setProxyAllowanceAsync(
 | 
			
		||||
                signedOrder.makerAddress,
 | 
			
		||||
                signedOrder.makerFeeAssetData,
 | 
			
		||||
@@ -887,7 +968,7 @@ export class FillOrderCombinatorialUtils {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let takerFeeAllowance;
 | 
			
		||||
        switch (takerStateScenario.feeAllowance) {
 | 
			
		||||
        switch (fillScenario.takerStateScenario.feeAllowance) {
 | 
			
		||||
            case AllowanceAmountScenario.Higher:
 | 
			
		||||
                break; // Noop since this is already the default
 | 
			
		||||
 | 
			
		||||
@@ -908,9 +989,12 @@ export class FillOrderCombinatorialUtils {
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            default:
 | 
			
		||||
                throw errorUtils.spawnSwitchErr('takerStateScenario.feeAllowance', takerStateScenario.feeAllowance);
 | 
			
		||||
                throw errorUtils.spawnSwitchErr(
 | 
			
		||||
                    'takerStateScenario.feeAllowance',
 | 
			
		||||
                    fillScenario.takerStateScenario.feeAllowance,
 | 
			
		||||
                );
 | 
			
		||||
        }
 | 
			
		||||
        if (takerFeeAllowance !== undefined) {
 | 
			
		||||
        if (isTakerFeeAssetTakerAsset && takerFeeAllowance !== undefined) {
 | 
			
		||||
            await this.assetWrapper.setProxyAllowanceAsync(
 | 
			
		||||
                this.takerAddress,
 | 
			
		||||
                signedOrder.takerFeeAssetData,
 | 
			
		||||
@@ -920,12 +1004,9 @@ export class FillOrderCombinatorialUtils {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getTakerAssetFillAmountAsync(
 | 
			
		||||
    signedOrder: SignedOrder,
 | 
			
		||||
    takerAssetFillAmountScenario: TakerAssetFillAmountScenario,
 | 
			
		||||
): BigNumber {
 | 
			
		||||
function getTakerAssetFillAmount(signedOrder: SignedOrder, fillScenario: FillScenario): BigNumber {
 | 
			
		||||
    let takerAssetFillAmount;
 | 
			
		||||
    switch (takerAssetFillAmountScenario) {
 | 
			
		||||
    switch (fillScenario.takerAssetFillAmountScenario) {
 | 
			
		||||
        case TakerAssetFillAmountScenario.Zero:
 | 
			
		||||
            takerAssetFillAmount = new BigNumber(0);
 | 
			
		||||
            break;
 | 
			
		||||
@@ -939,20 +1020,42 @@ function getTakerAssetFillAmountAsync(
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case TakerAssetFillAmountScenario.LessThanTakerAssetAmount:
 | 
			
		||||
            const takerAssetProxyId = assetDataUtils.decodeAssetProxyId(signedOrder.takerAssetData);
 | 
			
		||||
            const makerAssetProxyId = assetDataUtils.decodeAssetProxyId(signedOrder.makerAssetData);
 | 
			
		||||
            const isEitherAssetERC721 =
 | 
			
		||||
                takerAssetProxyId === AssetProxyId.ERC721 || makerAssetProxyId === AssetProxyId.ERC721;
 | 
			
		||||
            if (isEitherAssetERC721) {
 | 
			
		||||
                throw new Error(
 | 
			
		||||
                    'Cannot test `TakerAssetFillAmountScenario.LessThanTakerAssetAmount` together with ERC721 assets since orders involving ERC721 must always be filled exactly.',
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
            // TODO(dorothy-zbornak): Do we need this?
 | 
			
		||||
            // const {
 | 
			
		||||
            //     makerAssetDataScenario,
 | 
			
		||||
            //     takerAssetDataScenario,
 | 
			
		||||
            //     makerFeeAssetDataScenario,
 | 
			
		||||
            //     takerFeeAssetDataScenario,
 | 
			
		||||
            // } = fillScenario.orderScenario;
 | 
			
		||||
            // const NFT_SCENARIOS = [
 | 
			
		||||
            //     AssetDataScenario.ERC721,
 | 
			
		||||
            //     AssetDataScenario.ERC1155NonFungible,
 | 
			
		||||
            // ];
 | 
			
		||||
            // const FEE_NFT_SCENARIOS = [
 | 
			
		||||
            //     FeeAssetDataScenario.ERC721,
 | 
			
		||||
            //     FeeAssetDataScenario.ERC1155NonFungible,
 | 
			
		||||
            // ];
 | 
			
		||||
            // if (_.includes(NFT_SCENARIOS, makerAssetDataScenario)) {
 | 
			
		||||
            //     FEE_NFT_SCENARIOS.push(FeeAssetDataScenario.MakerToken);
 | 
			
		||||
            // }
 | 
			
		||||
            // if (_.includes(NFT_SCENARIOS, takerAssetDataScenario)) {
 | 
			
		||||
            //     FEE_NFT_SCENARIOS.push(FeeAssetDataScenario.TakerToken);
 | 
			
		||||
            // }
 | 
			
		||||
            // const isAnyAssetNonFungible =
 | 
			
		||||
            //     _.includes(NFT_SCENARIOS, makerAssetDataScenario) ||
 | 
			
		||||
            //     _.includes(NFT_SCENARIOS, takerAssetDataScenario) ||
 | 
			
		||||
            //     _.includes(FEE_NFT_SCENARIOS, makerFeeAssetDataScenario) ||
 | 
			
		||||
            //     _.includes(FEE_NFT_SCENARIOS, takerFeeAssetDataScenario);
 | 
			
		||||
            // if (isAnyAssetNonFungible) {
 | 
			
		||||
            //     throw new Error(
 | 
			
		||||
            //         'Cannot test `TakerAssetFillAmountScenario.LessThanTakerAssetAmount` together with ERC721 assets since orders involving ERC721 must always be filled exactly.',
 | 
			
		||||
            //     );
 | 
			
		||||
            // }
 | 
			
		||||
            takerAssetFillAmount = signedOrder.takerAssetAmount.div(2).integerValue(BigNumber.ROUND_FLOOR);
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        default:
 | 
			
		||||
            throw errorUtils.spawnSwitchErr('TakerAssetFillAmountScenario', takerAssetFillAmountScenario);
 | 
			
		||||
            throw errorUtils.spawnSwitchErr('TakerAssetFillAmountScenario', fillScenario.takerAssetFillAmountScenario);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return takerAssetFillAmount;
 | 
			
		||||
@@ -971,11 +1074,7 @@ function fillErrorToRevertError(order: Order, error: FillOrderError): RevertErro
 | 
			
		||||
        case FillOrderError.InvalidFillPrice:
 | 
			
		||||
            return new ExchangeRevertErrors.FillError(ExchangeRevertErrors.FillErrorCode.InvalidFillPrice, orderHash);
 | 
			
		||||
        case FillOrderError.TransferFailed:
 | 
			
		||||
            return new ExchangeRevertErrors.AssetProxyTransferError(
 | 
			
		||||
                orderHash,
 | 
			
		||||
                undefined,
 | 
			
		||||
                new StringRevertError(FillOrderError.TransferFailed).encode(),
 | 
			
		||||
            );
 | 
			
		||||
            return new ExchangeRevertErrors.AssetProxyTransferError(orderHash);
 | 
			
		||||
        default:
 | 
			
		||||
            return new StringRevertError(error);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -27,6 +27,9 @@ export enum AssetDataScenario {
 | 
			
		||||
    ERC20FiveDecimals = 'ERC20_FIVE_DECIMALS',
 | 
			
		||||
    ERC20EighteenDecimals = 'ERC20_EIGHTEEN_DECIMALS',
 | 
			
		||||
    ERC721 = 'ERC721',
 | 
			
		||||
    ERC1155Fungible = 'ERC1155_FUNGIBLE',
 | 
			
		||||
    ERC1155NonFungible = 'ERC1155_NON_FUNGIBLE',
 | 
			
		||||
    MultiAssetERC20 = 'MULTI_ASSET_ERC20',
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export enum FeeAssetDataScenario {
 | 
			
		||||
@@ -34,6 +37,9 @@ export enum FeeAssetDataScenario {
 | 
			
		||||
    ERC20FiveDecimals = 'ERC20_FIVE_DECIMALS',
 | 
			
		||||
    ERC20EighteenDecimals = 'ERC20_EIGHTEEN_DECIMALS',
 | 
			
		||||
    ERC721 = 'ERC721',
 | 
			
		||||
    ERC1155Fungible = 'ERC1155_FUNGIBLE',
 | 
			
		||||
    ERC1155NonFungible = 'ERC1155_NON_FUNGIBLE',
 | 
			
		||||
    MultiAssetERC20 = 'MULTI_ASSET_ERC20',
 | 
			
		||||
    MakerToken = 'MAKER_TOKEN',
 | 
			
		||||
    TakerToken = 'TAKER_TOKEN',
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
import { DummyERC721TokenContract } from '@0x/contracts-erc721';
 | 
			
		||||
import { constants, ERC721TokenIdsByOwner } from '@0x/contracts-test-utils';
 | 
			
		||||
import { constants, ERC1155HoldingsByOwner, ERC721TokenIdsByOwner } from '@0x/contracts-test-utils';
 | 
			
		||||
import { assetDataUtils, generatePseudoRandomSalt } from '@0x/order-utils';
 | 
			
		||||
import { Order } from '@0x/types';
 | 
			
		||||
import { BigNumber, errorUtils } from '@0x/utils';
 | 
			
		||||
import * as _ from 'lodash';
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
    AssetDataScenario,
 | 
			
		||||
@@ -17,13 +17,16 @@ import {
 | 
			
		||||
const TEN_UNITS_EIGHTEEN_DECIMALS = new BigNumber('10e18');
 | 
			
		||||
const FIVE_UNITS_EIGHTEEN_DECIMALS = new BigNumber('5e18');
 | 
			
		||||
const POINT_ONE_UNITS_EIGHTEEN_DECIMALS = new BigNumber('0.1e18');
 | 
			
		||||
const ONE_UNITS_EIGHTEEN_DECIMALS = new BigNumber('1e18');
 | 
			
		||||
const POINT_ZERO_FIVE_UNITS_EIGHTEEN_DECIMALS = new BigNumber('0.05e18');
 | 
			
		||||
const TEN_UNITS_FIVE_DECIMALS = new BigNumber('10e5');
 | 
			
		||||
const FIVE_UNITS_FIVE_DECIMALS = new BigNumber('5e5');
 | 
			
		||||
const POINT_ONE_UNITS_FIVE_DECIMALS = new BigNumber('0.1e5');
 | 
			
		||||
const ONE_UNITS_FIVE_DECIMALS = new BigNumber('1e5');
 | 
			
		||||
const POINT_ZERO_FIVE_UNITS_FIVE_DECIMALS = new BigNumber('0.05e5');
 | 
			
		||||
const TEN_UNITS_ZERO_DECIMALS = new BigNumber(10);
 | 
			
		||||
const ONE_THOUSAND_UNITS_ZERO_DECIMALS = new BigNumber(1000);
 | 
			
		||||
const TEN_UNITS_ZERO_DECIMALS = new BigNumber(10);
 | 
			
		||||
const FIVE_UNITS_ZERO_DECIMALS = new BigNumber(5);
 | 
			
		||||
const ONE_UNITS_ZERO_DECIMALS = new BigNumber(1);
 | 
			
		||||
const ONE_NFT_UNIT = new BigNumber(1);
 | 
			
		||||
const ZERO_UNITS = new BigNumber(0);
 | 
			
		||||
@@ -33,8 +36,10 @@ export class OrderFactoryFromScenario {
 | 
			
		||||
    private readonly _erc20EighteenDecimalTokenAddresses: string[];
 | 
			
		||||
    private readonly _erc20FiveDecimalTokenAddresses: string[];
 | 
			
		||||
    private readonly _erc20ZeroDecimalTokenAddresses: string[];
 | 
			
		||||
    private readonly _erc721Token: DummyERC721TokenContract;
 | 
			
		||||
    private readonly _erc721TokenAddress: string;
 | 
			
		||||
    private readonly _erc1155TokenAddress: string;
 | 
			
		||||
    private readonly _erc721Balances: ERC721TokenIdsByOwner;
 | 
			
		||||
    private readonly _erc1155Holdings: ERC1155HoldingsByOwner;
 | 
			
		||||
    private readonly _exchangeAddress: string;
 | 
			
		||||
    private readonly _chainId: number;
 | 
			
		||||
    constructor(
 | 
			
		||||
@@ -42,8 +47,10 @@ export class OrderFactoryFromScenario {
 | 
			
		||||
        erc20EighteenDecimalTokenAddresses: string[],
 | 
			
		||||
        erc20FiveDecimalTokenAddresses: string[],
 | 
			
		||||
        erc20ZeroDecimalTokenAddresses: string[],
 | 
			
		||||
        erc721Token: DummyERC721TokenContract,
 | 
			
		||||
        erc721TokenAddress: string,
 | 
			
		||||
        erc1155TokenAddress: string,
 | 
			
		||||
        erc721Balances: ERC721TokenIdsByOwner,
 | 
			
		||||
        erc1155Holdings: ERC1155HoldingsByOwner,
 | 
			
		||||
        exchangeAddress: string,
 | 
			
		||||
        chainId: number,
 | 
			
		||||
    ) {
 | 
			
		||||
@@ -51,16 +58,31 @@ export class OrderFactoryFromScenario {
 | 
			
		||||
        this._erc20EighteenDecimalTokenAddresses = erc20EighteenDecimalTokenAddresses;
 | 
			
		||||
        this._erc20FiveDecimalTokenAddresses = erc20FiveDecimalTokenAddresses;
 | 
			
		||||
        this._erc20ZeroDecimalTokenAddresses = erc20ZeroDecimalTokenAddresses;
 | 
			
		||||
        this._erc721Token = erc721Token;
 | 
			
		||||
        this._erc721TokenAddress = erc721TokenAddress;
 | 
			
		||||
        this._erc1155TokenAddress = erc1155TokenAddress;
 | 
			
		||||
        this._erc721Balances = erc721Balances;
 | 
			
		||||
        this._erc1155Holdings = erc1155Holdings;
 | 
			
		||||
        this._exchangeAddress = exchangeAddress;
 | 
			
		||||
        this._chainId = chainId;
 | 
			
		||||
    }
 | 
			
		||||
    public generateOrder(orderScenario: OrderScenario): Order {
 | 
			
		||||
        const makerAddress = this._userAddresses[1];
 | 
			
		||||
        let takerAddress = this._userAddresses[2];
 | 
			
		||||
        const erc721MakerAssetIds = this._erc721Balances[makerAddress][this._erc721Token.address];
 | 
			
		||||
        const erc721TakerAssetIds = this._erc721Balances[takerAddress][this._erc721Token.address];
 | 
			
		||||
        const erc721MakerAssetIds = this._erc721Balances[makerAddress][this._erc721TokenAddress];
 | 
			
		||||
        const erc721TakerAssetIds = this._erc721Balances[takerAddress][this._erc721TokenAddress];
 | 
			
		||||
        const erc1155FungibleMakerTokenIds = getERC1155FungibleOwnerTokenIds(
 | 
			
		||||
            this._erc1155Holdings.fungible[makerAddress][this._erc1155TokenAddress],
 | 
			
		||||
        );
 | 
			
		||||
        const erc1155NonFungibleMakerTokenIds = getERC1155NonFungibleOwnerTokenIds(
 | 
			
		||||
            this._erc1155Holdings.nonFungible[makerAddress][this._erc1155TokenAddress],
 | 
			
		||||
        );
 | 
			
		||||
        const erc1155FungibleTakerTokenIds = getERC1155FungibleOwnerTokenIds(
 | 
			
		||||
            this._erc1155Holdings.fungible[takerAddress][this._erc1155TokenAddress],
 | 
			
		||||
        );
 | 
			
		||||
        const erc1155NonFungibleTakerTokenIds = getERC1155NonFungibleOwnerTokenIds(
 | 
			
		||||
            this._erc1155Holdings.nonFungible[takerAddress][this._erc1155TokenAddress],
 | 
			
		||||
        );
 | 
			
		||||
        const erc1155CallbackData = constants.NULL_BYTES;
 | 
			
		||||
        let feeRecipientAddress;
 | 
			
		||||
        let makerAssetAmount;
 | 
			
		||||
        let takerAssetAmount;
 | 
			
		||||
@@ -91,14 +113,36 @@ export class OrderFactoryFromScenario {
 | 
			
		||||
                makerAssetData = assetDataUtils.encodeERC20AssetData(this._erc20FiveDecimalTokenAddresses[0]);
 | 
			
		||||
                break;
 | 
			
		||||
            case AssetDataScenario.ERC721:
 | 
			
		||||
                makerAssetData = assetDataUtils.encodeERC721AssetData(
 | 
			
		||||
                    this._erc721Token.address,
 | 
			
		||||
                    erc721MakerAssetIds[0],
 | 
			
		||||
                );
 | 
			
		||||
                makerAssetData = assetDataUtils.encodeERC721AssetData(this._erc721TokenAddress, erc721MakerAssetIds[0]);
 | 
			
		||||
                break;
 | 
			
		||||
            case AssetDataScenario.ERC20ZeroDecimals:
 | 
			
		||||
                makerAssetData = assetDataUtils.encodeERC20AssetData(this._erc20ZeroDecimalTokenAddresses[0]);
 | 
			
		||||
                break;
 | 
			
		||||
            case AssetDataScenario.ERC1155Fungible:
 | 
			
		||||
                makerAssetData = assetDataUtils.encodeERC1155AssetData(
 | 
			
		||||
                    this._erc1155TokenAddress,
 | 
			
		||||
                    [erc1155FungibleMakerTokenIds[0]],
 | 
			
		||||
                    [ONE_UNITS_ZERO_DECIMALS],
 | 
			
		||||
                    erc1155CallbackData,
 | 
			
		||||
                );
 | 
			
		||||
                break;
 | 
			
		||||
            case AssetDataScenario.ERC1155NonFungible:
 | 
			
		||||
                makerAssetData = assetDataUtils.encodeERC1155AssetData(
 | 
			
		||||
                    this._erc1155TokenAddress,
 | 
			
		||||
                    [erc1155NonFungibleMakerTokenIds[0]],
 | 
			
		||||
                    [ONE_UNITS_ZERO_DECIMALS],
 | 
			
		||||
                    erc1155CallbackData,
 | 
			
		||||
                );
 | 
			
		||||
                break;
 | 
			
		||||
            case AssetDataScenario.MultiAssetERC20:
 | 
			
		||||
                makerAssetData = assetDataUtils.encodeMultiAssetData(
 | 
			
		||||
                    [ONE_UNITS_EIGHTEEN_DECIMALS, ONE_UNITS_FIVE_DECIMALS],
 | 
			
		||||
                    [
 | 
			
		||||
                        assetDataUtils.encodeERC20AssetData(this._erc20EighteenDecimalTokenAddresses[0]),
 | 
			
		||||
                        assetDataUtils.encodeERC20AssetData(this._erc20FiveDecimalTokenAddresses[0]),
 | 
			
		||||
                    ],
 | 
			
		||||
                );
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                throw errorUtils.spawnSwitchErr('AssetDataScenario', orderScenario.makerAssetDataScenario);
 | 
			
		||||
        }
 | 
			
		||||
@@ -111,14 +155,36 @@ export class OrderFactoryFromScenario {
 | 
			
		||||
                takerAssetData = assetDataUtils.encodeERC20AssetData(this._erc20FiveDecimalTokenAddresses[1]);
 | 
			
		||||
                break;
 | 
			
		||||
            case AssetDataScenario.ERC721:
 | 
			
		||||
                takerAssetData = assetDataUtils.encodeERC721AssetData(
 | 
			
		||||
                    this._erc721Token.address,
 | 
			
		||||
                    erc721TakerAssetIds[0],
 | 
			
		||||
                );
 | 
			
		||||
                takerAssetData = assetDataUtils.encodeERC721AssetData(this._erc721TokenAddress, erc721TakerAssetIds[0]);
 | 
			
		||||
                break;
 | 
			
		||||
            case AssetDataScenario.ERC20ZeroDecimals:
 | 
			
		||||
                takerAssetData = assetDataUtils.encodeERC20AssetData(this._erc20ZeroDecimalTokenAddresses[1]);
 | 
			
		||||
                break;
 | 
			
		||||
            case AssetDataScenario.ERC1155Fungible:
 | 
			
		||||
                takerAssetData = assetDataUtils.encodeERC1155AssetData(
 | 
			
		||||
                    this._erc1155TokenAddress,
 | 
			
		||||
                    [erc1155FungibleTakerTokenIds[1]],
 | 
			
		||||
                    [ONE_UNITS_ZERO_DECIMALS],
 | 
			
		||||
                    erc1155CallbackData,
 | 
			
		||||
                );
 | 
			
		||||
                break;
 | 
			
		||||
            case AssetDataScenario.ERC1155NonFungible:
 | 
			
		||||
                takerAssetData = assetDataUtils.encodeERC1155AssetData(
 | 
			
		||||
                    this._erc1155TokenAddress,
 | 
			
		||||
                    [erc1155NonFungibleTakerTokenIds[0]],
 | 
			
		||||
                    [ONE_UNITS_ZERO_DECIMALS],
 | 
			
		||||
                    erc1155CallbackData,
 | 
			
		||||
                );
 | 
			
		||||
                break;
 | 
			
		||||
            case AssetDataScenario.MultiAssetERC20:
 | 
			
		||||
                takerAssetData = assetDataUtils.encodeMultiAssetData(
 | 
			
		||||
                    [ONE_UNITS_EIGHTEEN_DECIMALS, ONE_UNITS_FIVE_DECIMALS],
 | 
			
		||||
                    [
 | 
			
		||||
                        assetDataUtils.encodeERC20AssetData(this._erc20EighteenDecimalTokenAddresses[1]),
 | 
			
		||||
                        assetDataUtils.encodeERC20AssetData(this._erc20FiveDecimalTokenAddresses[1]),
 | 
			
		||||
                    ],
 | 
			
		||||
                );
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                throw errorUtils.spawnSwitchErr('AssetDataScenario', orderScenario.takerAssetDataScenario);
 | 
			
		||||
        }
 | 
			
		||||
@@ -127,17 +193,22 @@ export class OrderFactoryFromScenario {
 | 
			
		||||
            case OrderAssetAmountScenario.Large:
 | 
			
		||||
                switch (orderScenario.makerAssetDataScenario) {
 | 
			
		||||
                    case AssetDataScenario.ERC20EighteenDecimals:
 | 
			
		||||
                    case AssetDataScenario.ERC1155Fungible:
 | 
			
		||||
                        makerAssetAmount = TEN_UNITS_EIGHTEEN_DECIMALS;
 | 
			
		||||
                        break;
 | 
			
		||||
                    case AssetDataScenario.ERC20FiveDecimals:
 | 
			
		||||
                        makerAssetAmount = TEN_UNITS_FIVE_DECIMALS;
 | 
			
		||||
                        break;
 | 
			
		||||
                    case AssetDataScenario.ERC721:
 | 
			
		||||
                    case AssetDataScenario.ERC1155NonFungible:
 | 
			
		||||
                        makerAssetAmount = ONE_NFT_UNIT;
 | 
			
		||||
                        break;
 | 
			
		||||
                    case AssetDataScenario.ERC20ZeroDecimals:
 | 
			
		||||
                        makerAssetAmount = ONE_THOUSAND_UNITS_ZERO_DECIMALS;
 | 
			
		||||
                        break;
 | 
			
		||||
                    case AssetDataScenario.MultiAssetERC20:
 | 
			
		||||
                        makerAssetAmount = TEN_UNITS_ZERO_DECIMALS;
 | 
			
		||||
                        break;
 | 
			
		||||
                    default:
 | 
			
		||||
                        throw errorUtils.spawnSwitchErr('AssetDataScenario', orderScenario.makerAssetDataScenario);
 | 
			
		||||
                }
 | 
			
		||||
@@ -145,17 +216,22 @@ export class OrderFactoryFromScenario {
 | 
			
		||||
            case OrderAssetAmountScenario.Small:
 | 
			
		||||
                switch (orderScenario.makerAssetDataScenario) {
 | 
			
		||||
                    case AssetDataScenario.ERC20EighteenDecimals:
 | 
			
		||||
                    case AssetDataScenario.ERC1155Fungible:
 | 
			
		||||
                        makerAssetAmount = FIVE_UNITS_EIGHTEEN_DECIMALS;
 | 
			
		||||
                        break;
 | 
			
		||||
                    case AssetDataScenario.ERC20FiveDecimals:
 | 
			
		||||
                        makerAssetAmount = FIVE_UNITS_FIVE_DECIMALS;
 | 
			
		||||
                        break;
 | 
			
		||||
                    case AssetDataScenario.ERC721:
 | 
			
		||||
                    case AssetDataScenario.ERC1155NonFungible:
 | 
			
		||||
                        makerAssetAmount = ONE_NFT_UNIT;
 | 
			
		||||
                        break;
 | 
			
		||||
                    case AssetDataScenario.ERC20ZeroDecimals:
 | 
			
		||||
                        makerAssetAmount = TEN_UNITS_ZERO_DECIMALS;
 | 
			
		||||
                        break;
 | 
			
		||||
                    case AssetDataScenario.MultiAssetERC20:
 | 
			
		||||
                        makerAssetAmount = ONE_UNITS_ZERO_DECIMALS;
 | 
			
		||||
                        break;
 | 
			
		||||
                    default:
 | 
			
		||||
                        throw errorUtils.spawnSwitchErr('AssetDataScenario', orderScenario.makerAssetDataScenario);
 | 
			
		||||
                }
 | 
			
		||||
@@ -171,17 +247,22 @@ export class OrderFactoryFromScenario {
 | 
			
		||||
            case OrderAssetAmountScenario.Large:
 | 
			
		||||
                switch (orderScenario.takerAssetDataScenario) {
 | 
			
		||||
                    case AssetDataScenario.ERC20EighteenDecimals:
 | 
			
		||||
                    case AssetDataScenario.ERC1155Fungible:
 | 
			
		||||
                        takerAssetAmount = TEN_UNITS_EIGHTEEN_DECIMALS;
 | 
			
		||||
                        break;
 | 
			
		||||
                    case AssetDataScenario.ERC20FiveDecimals:
 | 
			
		||||
                        takerAssetAmount = TEN_UNITS_FIVE_DECIMALS;
 | 
			
		||||
                        break;
 | 
			
		||||
                    case AssetDataScenario.ERC721:
 | 
			
		||||
                    case AssetDataScenario.ERC1155NonFungible:
 | 
			
		||||
                        takerAssetAmount = ONE_NFT_UNIT;
 | 
			
		||||
                        break;
 | 
			
		||||
                    case AssetDataScenario.ERC20ZeroDecimals:
 | 
			
		||||
                        takerAssetAmount = ONE_THOUSAND_UNITS_ZERO_DECIMALS;
 | 
			
		||||
                        break;
 | 
			
		||||
                    case AssetDataScenario.MultiAssetERC20:
 | 
			
		||||
                        takerAssetAmount = TEN_UNITS_ZERO_DECIMALS;
 | 
			
		||||
                        break;
 | 
			
		||||
                    default:
 | 
			
		||||
                        throw errorUtils.spawnSwitchErr('AssetDataScenario', orderScenario.takerAssetDataScenario);
 | 
			
		||||
                }
 | 
			
		||||
@@ -189,17 +270,22 @@ export class OrderFactoryFromScenario {
 | 
			
		||||
            case OrderAssetAmountScenario.Small:
 | 
			
		||||
                switch (orderScenario.takerAssetDataScenario) {
 | 
			
		||||
                    case AssetDataScenario.ERC20EighteenDecimals:
 | 
			
		||||
                    case AssetDataScenario.ERC1155Fungible:
 | 
			
		||||
                        takerAssetAmount = FIVE_UNITS_EIGHTEEN_DECIMALS;
 | 
			
		||||
                        break;
 | 
			
		||||
                    case AssetDataScenario.ERC20FiveDecimals:
 | 
			
		||||
                        takerAssetAmount = FIVE_UNITS_FIVE_DECIMALS;
 | 
			
		||||
                        break;
 | 
			
		||||
                    case AssetDataScenario.ERC721:
 | 
			
		||||
                    case AssetDataScenario.ERC1155NonFungible:
 | 
			
		||||
                        takerAssetAmount = ONE_NFT_UNIT;
 | 
			
		||||
                        break;
 | 
			
		||||
                    case AssetDataScenario.ERC20ZeroDecimals:
 | 
			
		||||
                        takerAssetAmount = TEN_UNITS_ZERO_DECIMALS;
 | 
			
		||||
                        break;
 | 
			
		||||
                    case AssetDataScenario.MultiAssetERC20:
 | 
			
		||||
                        takerAssetAmount = ONE_UNITS_ZERO_DECIMALS;
 | 
			
		||||
                        break;
 | 
			
		||||
                    default:
 | 
			
		||||
                        throw errorUtils.spawnSwitchErr('AssetDataScenario', orderScenario.takerAssetDataScenario);
 | 
			
		||||
                }
 | 
			
		||||
@@ -218,6 +304,8 @@ export class OrderFactoryFromScenario {
 | 
			
		||||
            erc20FiveDecimalTokenAddress: string,
 | 
			
		||||
            erc20ZeroDecimalTokenAddress: string,
 | 
			
		||||
            erc721AssetId: BigNumber,
 | 
			
		||||
            erc1155FungibleTokenId: BigNumber,
 | 
			
		||||
            erc1155NonFungibleAssetId: BigNumber,
 | 
			
		||||
        ): [BigNumber, string] => {
 | 
			
		||||
            const feeAmount = getFeeAmountFromScenario(orderScenario, feeAssetDataScenario, feeAmountScenario);
 | 
			
		||||
            switch (feeAssetDataScenario) {
 | 
			
		||||
@@ -232,7 +320,38 @@ export class OrderFactoryFromScenario {
 | 
			
		||||
                case FeeAssetDataScenario.ERC20ZeroDecimals:
 | 
			
		||||
                    return [feeAmount, assetDataUtils.encodeERC20AssetData(erc20ZeroDecimalTokenAddress)];
 | 
			
		||||
                case FeeAssetDataScenario.ERC721:
 | 
			
		||||
                    return [feeAmount, assetDataUtils.encodeERC721AssetData(this._erc721Token.address, erc721AssetId)];
 | 
			
		||||
                    return [feeAmount, assetDataUtils.encodeERC721AssetData(this._erc721TokenAddress, erc721AssetId)];
 | 
			
		||||
                case FeeAssetDataScenario.ERC1155Fungible:
 | 
			
		||||
                    return [
 | 
			
		||||
                        feeAmount,
 | 
			
		||||
                        assetDataUtils.encodeERC1155AssetData(
 | 
			
		||||
                            this._erc1155TokenAddress,
 | 
			
		||||
                            [erc1155FungibleTokenId],
 | 
			
		||||
                            [ONE_UNITS_ZERO_DECIMALS],
 | 
			
		||||
                            erc1155CallbackData,
 | 
			
		||||
                        ),
 | 
			
		||||
                    ];
 | 
			
		||||
                case FeeAssetDataScenario.ERC1155NonFungible:
 | 
			
		||||
                    return [
 | 
			
		||||
                        feeAmount,
 | 
			
		||||
                        assetDataUtils.encodeERC1155AssetData(
 | 
			
		||||
                            this._erc1155TokenAddress,
 | 
			
		||||
                            [erc1155NonFungibleAssetId],
 | 
			
		||||
                            [ONE_UNITS_ZERO_DECIMALS],
 | 
			
		||||
                            erc1155CallbackData,
 | 
			
		||||
                        ),
 | 
			
		||||
                    ];
 | 
			
		||||
                case FeeAssetDataScenario.MultiAssetERC20:
 | 
			
		||||
                    return [
 | 
			
		||||
                        feeAmount,
 | 
			
		||||
                        assetDataUtils.encodeMultiAssetData(
 | 
			
		||||
                            [POINT_ZERO_FIVE_UNITS_EIGHTEEN_DECIMALS, POINT_ZERO_FIVE_UNITS_FIVE_DECIMALS],
 | 
			
		||||
                            [
 | 
			
		||||
                                assetDataUtils.encodeERC20AssetData(erc20EighteenDecimalTokenAddress),
 | 
			
		||||
                                assetDataUtils.encodeERC20AssetData(erc20FiveDecimalTokenAddress),
 | 
			
		||||
                            ],
 | 
			
		||||
                        ),
 | 
			
		||||
                    ];
 | 
			
		||||
                default:
 | 
			
		||||
                    throw errorUtils.spawnSwitchErr('FeeAssetDataScenario', feeAssetDataScenario);
 | 
			
		||||
            }
 | 
			
		||||
@@ -245,6 +364,8 @@ export class OrderFactoryFromScenario {
 | 
			
		||||
            this._erc20FiveDecimalTokenAddresses[2],
 | 
			
		||||
            this._erc20ZeroDecimalTokenAddresses[2],
 | 
			
		||||
            erc721MakerAssetIds[1],
 | 
			
		||||
            erc1155FungibleMakerTokenIds[2],
 | 
			
		||||
            erc1155NonFungibleMakerTokenIds[1],
 | 
			
		||||
        );
 | 
			
		||||
        [takerFee, takerFeeAssetData] = feeFromScenario(
 | 
			
		||||
            orderScenario.takerFeeScenario,
 | 
			
		||||
@@ -253,6 +374,8 @@ export class OrderFactoryFromScenario {
 | 
			
		||||
            this._erc20FiveDecimalTokenAddresses[3],
 | 
			
		||||
            this._erc20ZeroDecimalTokenAddresses[3],
 | 
			
		||||
            erc721TakerAssetIds[1],
 | 
			
		||||
            erc1155FungibleTakerTokenIds[3],
 | 
			
		||||
            erc1155NonFungibleTakerTokenIds[1],
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        switch (orderScenario.expirationTimeSecondsScenario) {
 | 
			
		||||
@@ -318,6 +441,7 @@ function getFeeAmountFromScenario(
 | 
			
		||||
): BigNumber {
 | 
			
		||||
    switch (feeAssetDataScenario) {
 | 
			
		||||
        case FeeAssetDataScenario.ERC721:
 | 
			
		||||
        case FeeAssetDataScenario.ERC1155NonFungible:
 | 
			
		||||
            switch (feeAmountScenario) {
 | 
			
		||||
                case OrderAssetAmountScenario.Zero:
 | 
			
		||||
                    return ZERO_UNITS;
 | 
			
		||||
@@ -349,6 +473,7 @@ function getFeeAmountFromScenario(
 | 
			
		||||
                    throw errorUtils.spawnSwitchErr('OrderAssetAmountScenario', feeAmountScenario);
 | 
			
		||||
            }
 | 
			
		||||
        case FeeAssetDataScenario.ERC20EighteenDecimals:
 | 
			
		||||
        case FeeAssetDataScenario.ERC1155Fungible:
 | 
			
		||||
            switch (feeAmountScenario) {
 | 
			
		||||
                case OrderAssetAmountScenario.Zero:
 | 
			
		||||
                    return ZERO_UNITS;
 | 
			
		||||
@@ -359,6 +484,17 @@ function getFeeAmountFromScenario(
 | 
			
		||||
                default:
 | 
			
		||||
                    throw errorUtils.spawnSwitchErr('OrderAssetAmountScenario', feeAmountScenario);
 | 
			
		||||
            }
 | 
			
		||||
        case FeeAssetDataScenario.MultiAssetERC20:
 | 
			
		||||
            switch (feeAmountScenario) {
 | 
			
		||||
                case OrderAssetAmountScenario.Zero:
 | 
			
		||||
                    return ZERO_UNITS;
 | 
			
		||||
                case OrderAssetAmountScenario.Small:
 | 
			
		||||
                    return ONE_UNITS_ZERO_DECIMALS;
 | 
			
		||||
                case OrderAssetAmountScenario.Large:
 | 
			
		||||
                    return FIVE_UNITS_ZERO_DECIMALS;
 | 
			
		||||
                default:
 | 
			
		||||
                    throw errorUtils.spawnSwitchErr('OrderAssetAmountScenario', feeAmountScenario);
 | 
			
		||||
            }
 | 
			
		||||
        case FeeAssetDataScenario.MakerToken:
 | 
			
		||||
            return getFeeAmountFromScenario(orderScenario, orderScenario.makerAssetDataScenario, feeAmountScenario);
 | 
			
		||||
        case FeeAssetDataScenario.TakerToken:
 | 
			
		||||
@@ -367,3 +503,13 @@ function getFeeAmountFromScenario(
 | 
			
		||||
            throw errorUtils.spawnSwitchErr('FeeAssetDataScenario', feeAssetDataScenario);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getERC1155FungibleOwnerTokenIds(holdings: { [tokenId: string]: BigNumber }): BigNumber[] {
 | 
			
		||||
    return _.keys(holdings).map(id => new BigNumber(id));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getERC1155NonFungibleOwnerTokenIds(holdings: { [tokenId: string]: BigNumber[] }): BigNumber[] {
 | 
			
		||||
    return _.values(holdings).map(group => group[0]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// tslint:disable: max-file-line-count
 | 
			
		||||
 
 | 
			
		||||
@@ -1,26 +0,0 @@
 | 
			
		||||
import { AbstractOrderFilledCancelledFetcher, orderHashUtils } from '@0x/order-utils';
 | 
			
		||||
import { SignedOrder } from '@0x/types';
 | 
			
		||||
import { BigNumber } from '@0x/utils';
 | 
			
		||||
 | 
			
		||||
import { ExchangeWrapper } from './exchange_wrapper';
 | 
			
		||||
 | 
			
		||||
export class SimpleOrderFilledCancelledFetcher implements AbstractOrderFilledCancelledFetcher {
 | 
			
		||||
    private readonly _exchangeWrapper: ExchangeWrapper;
 | 
			
		||||
    constructor(exchange: ExchangeWrapper) {
 | 
			
		||||
        this._exchangeWrapper = exchange;
 | 
			
		||||
    }
 | 
			
		||||
    public async getFilledTakerAmountAsync(orderHash: string): Promise<BigNumber> {
 | 
			
		||||
        const filledTakerAmount = new BigNumber(await this._exchangeWrapper.getTakerAssetFilledAmountAsync(orderHash));
 | 
			
		||||
        return filledTakerAmount;
 | 
			
		||||
    }
 | 
			
		||||
    public async isOrderCancelledAsync(signedOrder: SignedOrder): Promise<boolean> {
 | 
			
		||||
        const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
 | 
			
		||||
        const isCancelled = await this._exchangeWrapper.isCancelledAsync(orderHash);
 | 
			
		||||
        const orderEpoch = await this._exchangeWrapper.getOrderEpochAsync(
 | 
			
		||||
            signedOrder.makerAddress,
 | 
			
		||||
            signedOrder.senderAddress,
 | 
			
		||||
        );
 | 
			
		||||
        const isCancelledByOrderEpoch = orderEpoch > signedOrder.salt;
 | 
			
		||||
        return isCancelled || isCancelledByOrderEpoch;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user