@0x/contracts-exchange: Add MultiAssetProxy, ERC1155Fungible, and ERC1155NonFungible combinatorial tests.

This commit is contained in:
Lawrence Forman
2019-05-23 16:06:40 -04:00
committed by Amir Bandeali
parent 741fdfa52e
commit 85ea291745
6 changed files with 955 additions and 353 deletions

View File

@@ -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

View File

@@ -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:

View File

@@ -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);
}

View File

@@ -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',
}

View File

@@ -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

View File

@@ -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;
}
}