update tests to use RevertErrors
This commit is contained in:
@@ -245,10 +245,10 @@ contract MixinExchangeWrapper is
|
||||
}
|
||||
}
|
||||
|
||||
require(
|
||||
totalFillResults.makerAssetFilledAmount >= makerAssetFillAmount,
|
||||
"COMPLETE_FILL_FAILED"
|
||||
);
|
||||
if (totalFillResults.makerAssetFilledAmount < makerAssetFillAmount) {
|
||||
LibRichErrors._rrevert(LibForwarderRichErrors.CompleteFillFailedError());
|
||||
}
|
||||
|
||||
return (totalFillResults, wethSpentAmount, makerAssetAcquiredAmount);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,10 +89,7 @@ contract MixinWeth is
|
||||
|
||||
// Ensure fee is less than amount of WETH remaining.
|
||||
if (ethFee > wethRemaining) {
|
||||
LibRichErrors._rrevert(LibForwarderRichErrors.InsufficientEthRemainingError(
|
||||
ethFee,
|
||||
wethRemaining
|
||||
));
|
||||
LibRichErrors._rrevert(LibForwarderRichErrors.InsufficientEthForFeeError());
|
||||
}
|
||||
|
||||
// Do nothing if no WETH remaining
|
||||
|
||||
@@ -23,10 +23,6 @@ import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
|
||||
|
||||
library LibForwarderRichErrors {
|
||||
|
||||
// bytes4(keccak256("UnregisteredAssetProxyError()"))
|
||||
bytes4 internal constant UNREGISTERED_ASSET_PROXY_ERROR_SELECTOR =
|
||||
0xf3b96b8d;
|
||||
|
||||
// bytes4(keccak256("UnregisteredAssetProxyError()"))
|
||||
bytes internal constant UNREGISTERED_ASSET_PROXY_ERROR =
|
||||
hex"f3b96b8d";
|
||||
@@ -35,9 +31,9 @@ library LibForwarderRichErrors {
|
||||
bytes4 internal constant UNSUPPORTED_ASSET_PROXY_ERROR_SELECTOR =
|
||||
0x7996a271;
|
||||
|
||||
// bytes4(keccak256("CompleteFillFailedError(uint256,uint256)"))
|
||||
bytes4 internal constant COMPLETE_FILL_FAILED_ERROR_SELECTOR =
|
||||
0x7675a605;
|
||||
// bytes4(keccak256("CompleteFillFailedError()"))
|
||||
bytes internal constant COMPLETE_FILL_FAILED_ERROR =
|
||||
hex"4bcb2058";
|
||||
|
||||
// bytes4(keccak256("MakerAssetMismatchError(bytes,bytes)"))
|
||||
bytes4 internal constant MAKER_ASSET_MISMATCH_ERROR_SELECTOR =
|
||||
@@ -47,18 +43,14 @@ library LibForwarderRichErrors {
|
||||
bytes4 internal constant FEE_PERCENTAGE_TOO_LARGE_ERROR_SELECTOR =
|
||||
0x1174fb80;
|
||||
|
||||
// bytes4(keccak256("InsufficientEthRemainingError(uint256,uint256)"))
|
||||
bytes4 internal constant INSUFFICIENT_ETH_REMAINING_ERROR_SELECTOR =
|
||||
0x01b718a6;
|
||||
// bytes4(keccak256("InsufficientEthForFeeError()"))
|
||||
bytes internal constant INSUFFICIENT_ETH_FOR_FEE_ERROR =
|
||||
hex"54a53d9e";
|
||||
|
||||
// bytes4(keccak256("OversoldWethError(uint256,uint256)"))
|
||||
bytes4 internal constant OVERSOLD_WETH_ERROR_SELECTOR =
|
||||
0x5cc555c8;
|
||||
|
||||
// bytes4(keccak256("TransferFailedError()"))
|
||||
bytes4 internal constant TRANSFER_FAILED_ERROR_SELECTOR =
|
||||
0x570f1df4;
|
||||
|
||||
// bytes4(keccak256("TransferFailedError()"))
|
||||
bytes internal constant TRANSFER_FAILED_ERROR =
|
||||
hex"570f1df4";
|
||||
@@ -101,19 +93,12 @@ library LibForwarderRichErrors {
|
||||
);
|
||||
}
|
||||
|
||||
function CompleteFillFailedError(
|
||||
uint256 desiredFillAmount,
|
||||
uint256 actualFillAmount
|
||||
)
|
||||
function CompleteFillFailedError()
|
||||
internal
|
||||
pure
|
||||
returns (bytes memory)
|
||||
{
|
||||
return abi.encodeWithSelector(
|
||||
COMPLETE_FILL_FAILED_ERROR_SELECTOR,
|
||||
desiredFillAmount,
|
||||
actualFillAmount
|
||||
);
|
||||
return COMPLETE_FILL_FAILED_ERROR;
|
||||
}
|
||||
|
||||
function MakerAssetMismatchError(
|
||||
@@ -144,19 +129,12 @@ library LibForwarderRichErrors {
|
||||
);
|
||||
}
|
||||
|
||||
function InsufficientEthRemainingError(
|
||||
uint256 ethFee,
|
||||
uint256 wethRemaining
|
||||
)
|
||||
function InsufficientEthForFeeError()
|
||||
internal
|
||||
pure
|
||||
returns (bytes memory)
|
||||
{
|
||||
return abi.encodeWithSelector(
|
||||
INSUFFICIENT_ETH_REMAINING_ERROR_SELECTOR,
|
||||
ethFee,
|
||||
wethRemaining
|
||||
);
|
||||
return INSUFFICIENT_ETH_FOR_FEE_ERROR;
|
||||
}
|
||||
|
||||
function OversoldWethError(
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
web3Wrapper,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||
import { assetDataUtils } from '@0x/order-utils';
|
||||
import { assetDataUtils, ForwarderRevertErrors } from '@0x/order-utils';
|
||||
import { RevertReason, SignedOrder } from '@0x/types';
|
||||
import { BigNumber, providerUtils } from '@0x/utils';
|
||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
@@ -123,12 +123,12 @@ describe(ContractName.Forwarder, () => {
|
||||
feeRecipientAddress: orderFeeRecipientAddress,
|
||||
makerAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
|
||||
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultTakerAssetAddress),
|
||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(200), DECIMALS_DEFAULT),
|
||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), DECIMALS_DEFAULT),
|
||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(200, DECIMALS_DEFAULT),
|
||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, DECIMALS_DEFAULT),
|
||||
makerFeeAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
|
||||
takerFeeAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
|
||||
makerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(0), DECIMALS_DEFAULT),
|
||||
takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(0), DECIMALS_DEFAULT),
|
||||
makerFee: Web3Wrapper.toBaseUnitAmount(0, DECIMALS_DEFAULT),
|
||||
takerFee: Web3Wrapper.toBaseUnitAmount(0, DECIMALS_DEFAULT),
|
||||
domain: {
|
||||
verifyingContractAddress: exchangeInstance.address,
|
||||
chainId,
|
||||
@@ -169,11 +169,11 @@ describe(ContractName.Forwarder, () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
orderWithoutFee = await orderFactory.newSignedOrderAsync();
|
||||
orderWithPercentageFee = await orderFactory.newSignedOrderAsync({
|
||||
takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), DECIMALS_DEFAULT),
|
||||
takerFee: Web3Wrapper.toBaseUnitAmount(1, DECIMALS_DEFAULT),
|
||||
takerFeeAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
|
||||
});
|
||||
orderWithWethFee = await orderFactory.newSignedOrderAsync({
|
||||
takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), DECIMALS_DEFAULT),
|
||||
takerFee: Web3Wrapper.toBaseUnitAmount(1, DECIMALS_DEFAULT),
|
||||
takerFeeAssetData: wethAssetData,
|
||||
});
|
||||
});
|
||||
@@ -189,16 +189,16 @@ describe(ContractName.Forwarder, () => {
|
||||
txDefaults,
|
||||
new BigNumber(chainId),
|
||||
);
|
||||
return expectContractCreationFailedAsync(
|
||||
(ForwarderContract.deployFrom0xArtifactAsync(
|
||||
artifacts.Forwarder,
|
||||
provider,
|
||||
txDefaults,
|
||||
exchangeInstance.address,
|
||||
wethAssetData,
|
||||
) as any) as sendTransactionResult,
|
||||
RevertReason.UnregisteredAssetProxy,
|
||||
);
|
||||
|
||||
const deployForwarder = (ForwarderContract.deployFrom0xArtifactAsync(
|
||||
artifacts.Forwarder,
|
||||
provider,
|
||||
txDefaults,
|
||||
exchangeInstance.address,
|
||||
wethAssetData,
|
||||
) as any) as sendTransactionResult;
|
||||
|
||||
expect(deployForwarder).to.revertWith(new ForwarderRevertErrors.UnregisteredAssetProxyError());
|
||||
});
|
||||
});
|
||||
describe('marketSellOrdersWithEth without extra fees', () => {
|
||||
@@ -215,7 +215,7 @@ describe(ContractName.Forwarder, () => {
|
||||
});
|
||||
it('should fill multiple orders with percentage fees', async () => {
|
||||
const secondOrderWithPercentageFee = await orderFactory.newSignedOrderAsync({
|
||||
takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), DECIMALS_DEFAULT),
|
||||
takerFee: Web3Wrapper.toBaseUnitAmount(2, DECIMALS_DEFAULT),
|
||||
takerFeeAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
|
||||
});
|
||||
const orders = [orderWithPercentageFee, secondOrderWithPercentageFee];
|
||||
@@ -225,7 +225,7 @@ describe(ContractName.Forwarder, () => {
|
||||
const unapprovedAsset = assetDataUtils.encodeERC20AssetData(secondErc20Token.address);
|
||||
const order = await orderFactory.newSignedOrderAsync({
|
||||
makerAssetData: unapprovedAsset,
|
||||
takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), DECIMALS_DEFAULT),
|
||||
takerFee: Web3Wrapper.toBaseUnitAmount(2, DECIMALS_DEFAULT),
|
||||
takerFeeAssetData: unapprovedAsset,
|
||||
});
|
||||
|
||||
@@ -266,7 +266,7 @@ describe(ContractName.Forwarder, () => {
|
||||
});
|
||||
it('should fill multiple orders with WETH fees', async () => {
|
||||
const secondOrderWithPercentageFee = await orderFactory.newSignedOrderAsync({
|
||||
takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), DECIMALS_DEFAULT),
|
||||
takerFee: Web3Wrapper.toBaseUnitAmount(2, DECIMALS_DEFAULT),
|
||||
takerFeeAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
|
||||
});
|
||||
const orders = [orderWithWethFee, secondOrderWithPercentageFee];
|
||||
@@ -287,16 +287,26 @@ describe(ContractName.Forwarder, () => {
|
||||
expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent));
|
||||
});
|
||||
it('should not fill orders with different makerAssetData than the first order', async () => {
|
||||
const firstOrderMakerAssetData = assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress);
|
||||
const erc20SignedOrder = await orderFactory.newSignedOrderAsync({
|
||||
makerAssetData: firstOrderMakerAssetData,
|
||||
});
|
||||
|
||||
const makerAssetId = erc721MakerAssetIds[0];
|
||||
const secondOrderMakerAssetData = assetDataUtils.encodeERC721AssetData(erc721Token.address, makerAssetId);
|
||||
const erc721SignedOrder = await orderFactory.newSignedOrderAsync({
|
||||
makerAssetAmount: new BigNumber(1),
|
||||
makerAssetData: assetDataUtils.encodeERC721AssetData(erc721Token.address, makerAssetId),
|
||||
makerAssetData: secondOrderMakerAssetData,
|
||||
});
|
||||
const erc20SignedOrder = await orderFactory.newSignedOrderAsync();
|
||||
|
||||
const orders = [erc20SignedOrder, erc721SignedOrder];
|
||||
|
||||
const revertError = new ForwarderRevertErrors.MakerAssetMismatchError(
|
||||
firstOrderMakerAssetData,
|
||||
secondOrderMakerAssetData,
|
||||
);
|
||||
await forwarderTestFactory.marketSellTestAsync(orders, new BigNumber(2), erc20Token, {
|
||||
revertReason: RevertReason.MakerAssetMismatch,
|
||||
revertError,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -307,9 +317,14 @@ describe(ContractName.Forwarder, () => {
|
||||
});
|
||||
});
|
||||
it('should fail if the fee is set too high', async () => {
|
||||
const forwarderFeePercentage = new BigNumber(6);
|
||||
const revertError = new ForwarderRevertErrors.FeePercentageTooLargeError(
|
||||
ForwarderTestFactory.getPercentageOfValue(constants.PERCENTAGE_DENOMINATOR, forwarderFeePercentage),
|
||||
);
|
||||
|
||||
await forwarderTestFactory.marketSellTestAsync([orderWithoutFee], new BigNumber(0.5), erc20Token, {
|
||||
forwarderFeePercentage: new BigNumber(6),
|
||||
revertReason: RevertReason.FeePercentageTooLarge,
|
||||
forwarderFeePercentage,
|
||||
revertError,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -324,7 +339,7 @@ describe(ContractName.Forwarder, () => {
|
||||
});
|
||||
it('should buy the exact amount of makerAsset and return excess ETH', async () => {
|
||||
await forwarderTestFactory.marketBuyTestAsync([orderWithoutFee], new BigNumber(0.5), erc20Token, {
|
||||
ethValueAdjustment: orderWithoutFee.takerAssetAmount.dividedToIntegerBy(2),
|
||||
ethValueAdjustment: new BigNumber(2),
|
||||
});
|
||||
});
|
||||
it('should buy the exact amount of makerAsset from a single order with a WETH fee', async () => {
|
||||
@@ -334,9 +349,10 @@ describe(ContractName.Forwarder, () => {
|
||||
await forwarderTestFactory.marketBuyTestAsync([orderWithPercentageFee], new BigNumber(0.5), erc20Token);
|
||||
});
|
||||
it('should revert if the amount of ETH sent is too low to fill the makerAssetAmount', async () => {
|
||||
const revertError = new ForwarderRevertErrors.CompleteFillFailedError();
|
||||
await forwarderTestFactory.marketBuyTestAsync([orderWithoutFee], new BigNumber(0.5), erc20Token, {
|
||||
ethValueAdjustment: new BigNumber(-1),
|
||||
revertReason: RevertReason.CompleteFillFailed,
|
||||
ethValueAdjustment: new BigNumber(-2),
|
||||
revertError,
|
||||
});
|
||||
});
|
||||
it('should buy an ERC721 asset from a single order', async () => {
|
||||
@@ -349,26 +365,12 @@ describe(ContractName.Forwarder, () => {
|
||||
makerAssetId,
|
||||
});
|
||||
});
|
||||
it('should revert if buying an ERC721 asset when later orders contain different makerAssetData', async () => {
|
||||
const makerAssetId = erc721MakerAssetIds[0];
|
||||
const erc721Order = await orderFactory.newSignedOrderAsync({
|
||||
makerAssetAmount: new BigNumber(1),
|
||||
makerAssetData: assetDataUtils.encodeERC721AssetData(erc721Token.address, makerAssetId),
|
||||
});
|
||||
const differentMakerAssetDataOrder = await orderFactory.newSignedOrderAsync();
|
||||
const orders = [erc721Order, differentMakerAssetDataOrder];
|
||||
|
||||
await forwarderTestFactory.marketBuyTestAsync(orders, new BigNumber(2), erc721Token, {
|
||||
makerAssetId,
|
||||
revertReason: RevertReason.MakerAssetMismatch,
|
||||
});
|
||||
});
|
||||
it('should buy an ERC721 asset and pay WETH fees from a single fee order', async () => {
|
||||
const makerAssetId = erc721MakerAssetIds[0];
|
||||
const erc721orderWithWethFee = await orderFactory.newSignedOrderAsync({
|
||||
makerAssetAmount: new BigNumber(1),
|
||||
makerAssetData: assetDataUtils.encodeERC721AssetData(erc721Token.address, makerAssetId),
|
||||
takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), DECIMALS_DEFAULT),
|
||||
takerFee: Web3Wrapper.toBaseUnitAmount(1, DECIMALS_DEFAULT),
|
||||
takerFeeAssetData: wethAssetData,
|
||||
});
|
||||
await forwarderTestFactory.marketBuyTestAsync([erc721orderWithWethFee], new BigNumber(1), erc721Token, {
|
||||
@@ -494,16 +496,20 @@ describe(ContractName.Forwarder, () => {
|
||||
});
|
||||
});
|
||||
it('should fail if the fee is set too high', async () => {
|
||||
const revertError = new ForwarderRevertErrors.FeePercentageTooLargeError(
|
||||
ForwarderTestFactory.getPercentageOfValue(constants.PERCENTAGE_DENOMINATOR, new BigNumber(6)),
|
||||
);
|
||||
await forwarderTestFactory.marketBuyTestAsync([orderWithoutFee], new BigNumber(0.5), erc20Token, {
|
||||
forwarderFeePercentage: new BigNumber(6),
|
||||
revertReason: RevertReason.FeePercentageTooLarge,
|
||||
revertError,
|
||||
});
|
||||
});
|
||||
it('should fail if there is not enough ETH remaining to pay the fee', async () => {
|
||||
const revertError = new ForwarderRevertErrors.InsufficientEthForFeeError();
|
||||
await forwarderTestFactory.marketBuyTestAsync([orderWithoutFee], new BigNumber(0.5), erc20Token, {
|
||||
ethValueAdjustment: new BigNumber(-1),
|
||||
ethValueAdjustment: new BigNumber(-2),
|
||||
forwarderFeePercentage: new BigNumber(2),
|
||||
revertReason: RevertReason.InsufficientEthRemaining,
|
||||
revertError,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,17 +1,10 @@
|
||||
import { ERC20Wrapper } from '@0x/contracts-asset-proxy';
|
||||
import { DummyERC20TokenContract } from '@0x/contracts-erc20';
|
||||
import { DummyERC721TokenContract } from '@0x/contracts-erc721';
|
||||
import {
|
||||
chaiSetup,
|
||||
constants,
|
||||
ERC20BalancesByOwner,
|
||||
expectTransactionFailedAsync,
|
||||
web3Wrapper,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { RevertReason, SignedOrder } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import { chaiSetup, constants, ERC20BalancesByOwner, web3Wrapper } from '@0x/contracts-test-utils';
|
||||
import { SignedOrder } from '@0x/types';
|
||||
import { BigNumber, RevertError } from '@0x/utils';
|
||||
import * as chai from 'chai';
|
||||
import { TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { ForwarderWrapper } from './forwarder_wrapper';
|
||||
@@ -43,49 +36,47 @@ function computeExpectedResults(orders: SignedOrder[], fractionalNumberOfOrdersT
|
||||
};
|
||||
let remainingOrdersToFill = fractionalNumberOfOrdersToFill;
|
||||
|
||||
_.forEach(
|
||||
orders,
|
||||
(order: SignedOrder): void => {
|
||||
if (remainingOrdersToFill.isEqualTo(constants.ZERO_AMOUNT)) {
|
||||
return;
|
||||
}
|
||||
for (const order of orders) {
|
||||
if (remainingOrdersToFill.isEqualTo(constants.ZERO_AMOUNT)) {
|
||||
break;
|
||||
}
|
||||
|
||||
let makerAssetAmount;
|
||||
let takerAssetAmount;
|
||||
let takerFee;
|
||||
if (remainingOrdersToFill.isLessThan(new BigNumber(1))) {
|
||||
const [partialFillNumerator, partialFillDenominator] = remainingOrdersToFill.toFraction();
|
||||
makerAssetAmount = order.makerAssetAmount
|
||||
.times(partialFillNumerator)
|
||||
.dividedToIntegerBy(partialFillDenominator);
|
||||
takerAssetAmount = order.takerAssetAmount
|
||||
.times(partialFillNumerator)
|
||||
.dividedToIntegerBy(partialFillDenominator);
|
||||
takerFee = order.takerFee.times(partialFillNumerator).dividedToIntegerBy(partialFillDenominator);
|
||||
} else {
|
||||
makerAssetAmount = order.makerAssetAmount;
|
||||
takerAssetAmount = order.takerAssetAmount;
|
||||
takerFee = order.takerFee;
|
||||
}
|
||||
let makerAssetAmount;
|
||||
let takerAssetAmount;
|
||||
let takerFee;
|
||||
if (remainingOrdersToFill.isLessThan(new BigNumber(1))) {
|
||||
const [partialFillNumerator, partialFillDenominator] = remainingOrdersToFill.toFraction();
|
||||
makerAssetAmount = order.makerAssetAmount
|
||||
.times(partialFillNumerator)
|
||||
.dividedToIntegerBy(partialFillDenominator);
|
||||
takerAssetAmount = order.takerAssetAmount
|
||||
.times(partialFillNumerator)
|
||||
.dividedToIntegerBy(partialFillDenominator);
|
||||
takerFee = order.takerFee.times(partialFillNumerator).dividedToIntegerBy(partialFillDenominator);
|
||||
|
||||
currentState.takerAssetFillAmount = currentState.takerAssetFillAmount.plus(takerAssetAmount);
|
||||
currentState.makerAssetFillAmount = currentState.makerAssetFillAmount.plus(makerAssetAmount);
|
||||
// Up to 1 wei worth of WETH will be oversold on the last order due to rounding
|
||||
currentState.maxOversoldWeth = new BigNumber(1);
|
||||
// Equivalently, up to 1 wei worth of maker asset will be overbought per order
|
||||
currentState.maxOverboughtMakerAsset = currentState.maxOversoldWeth
|
||||
.times(order.makerAssetAmount)
|
||||
.dividedToIntegerBy(order.takerAssetAmount);
|
||||
} else {
|
||||
makerAssetAmount = order.makerAssetAmount;
|
||||
takerAssetAmount = order.takerAssetAmount;
|
||||
takerFee = order.takerFee;
|
||||
}
|
||||
|
||||
if (order.takerFeeAssetData === order.makerAssetData) {
|
||||
currentState.percentageFees = currentState.percentageFees.plus(takerFee);
|
||||
} else if (order.takerFeeAssetData === order.takerAssetData) {
|
||||
currentState.wethFees = currentState.wethFees.plus(takerFee);
|
||||
// Up to 1 wei worth of WETH will be oversold per order due to rounding
|
||||
currentState.maxOversoldWeth = currentState.maxOversoldWeth.plus(new BigNumber(1));
|
||||
// Equivalently, up to 1 wei worth of maker asset will be overbought per order
|
||||
currentState.maxOverboughtMakerAsset = currentState.maxOversoldWeth
|
||||
.times(makerAssetAmount)
|
||||
.dividedToIntegerBy(takerAssetAmount);
|
||||
}
|
||||
currentState.takerAssetFillAmount = currentState.takerAssetFillAmount.plus(takerAssetAmount);
|
||||
currentState.makerAssetFillAmount = currentState.makerAssetFillAmount.plus(makerAssetAmount);
|
||||
|
||||
remainingOrdersToFill = BigNumber.max(remainingOrdersToFill.minus(new BigNumber(1)), constants.ZERO_AMOUNT);
|
||||
},
|
||||
);
|
||||
if (order.takerFeeAssetData === order.makerAssetData) {
|
||||
currentState.percentageFees = currentState.percentageFees.plus(takerFee);
|
||||
} else if (order.takerFeeAssetData === order.takerAssetData) {
|
||||
currentState.wethFees = currentState.wethFees.plus(takerFee);
|
||||
}
|
||||
|
||||
remainingOrdersToFill = BigNumber.max(remainingOrdersToFill.minus(new BigNumber(1)), constants.ZERO_AMOUNT);
|
||||
}
|
||||
|
||||
return currentState;
|
||||
}
|
||||
@@ -143,7 +134,7 @@ export class ForwarderTestFactory {
|
||||
ethValueAdjustment?: BigNumber; // Used to provided insufficient/excess ETH
|
||||
forwarderFeePercentage?: BigNumber;
|
||||
makerAssetId?: BigNumber;
|
||||
revertReason?: RevertReason;
|
||||
revertError?: RevertError;
|
||||
} = {},
|
||||
): Promise<void> {
|
||||
const ethValueAdjustment = options.ethValueAdjustment || constants.ZERO_AMOUNT;
|
||||
@@ -171,23 +162,7 @@ export class ForwarderTestFactory {
|
||||
.plus(ethSpentOnForwarderFee)
|
||||
.plus(ethValueAdjustment);
|
||||
|
||||
if (options.revertReason !== undefined) {
|
||||
await expectTransactionFailedAsync(
|
||||
this._forwarderWrapper.marketBuyOrdersWithEthAsync(
|
||||
orders,
|
||||
expectedResults.makerAssetFillAmount,
|
||||
{
|
||||
value: ethValue,
|
||||
from: this._takerAddress,
|
||||
},
|
||||
{ feePercentage, feeRecipient: this._forwarderFeeRecipientAddress },
|
||||
),
|
||||
options.revertReason,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const tx = await this._forwarderWrapper.marketBuyOrdersWithEthAsync(
|
||||
const tx = this._forwarderWrapper.marketBuyOrdersWithEthAsync(
|
||||
orders,
|
||||
expectedResults.makerAssetFillAmount,
|
||||
{
|
||||
@@ -197,11 +172,23 @@ export class ForwarderTestFactory {
|
||||
{ feePercentage, feeRecipient: this._forwarderFeeRecipientAddress },
|
||||
);
|
||||
|
||||
await this._checkResultsAsync(tx, expectedResults, takerEthBalanceBefore, erc20Balances, makerAssetContract, {
|
||||
forwarderFeePercentage,
|
||||
forwarderFeeRecipientEthBalanceBefore,
|
||||
makerAssetId: options.makerAssetId,
|
||||
});
|
||||
if (options.revertError !== undefined) {
|
||||
await expect(tx).to.revertWith(options.revertError);
|
||||
} else {
|
||||
const gasUsed = (await tx).gasUsed;
|
||||
await this._checkResultsAsync(
|
||||
gasUsed,
|
||||
expectedResults,
|
||||
takerEthBalanceBefore,
|
||||
erc20Balances,
|
||||
makerAssetContract,
|
||||
{
|
||||
forwarderFeePercentage,
|
||||
forwarderFeeRecipientEthBalanceBefore,
|
||||
makerAssetId: options.makerAssetId,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public async marketSellTestAsync(
|
||||
@@ -210,7 +197,7 @@ export class ForwarderTestFactory {
|
||||
makerAssetContract: DummyERC20TokenContract,
|
||||
options: {
|
||||
forwarderFeePercentage?: BigNumber;
|
||||
revertReason?: RevertReason;
|
||||
revertError?: RevertError;
|
||||
} = {},
|
||||
): Promise<void> {
|
||||
const forwarderFeePercentage = options.forwarderFeePercentage || constants.ZERO_AMOUNT;
|
||||
@@ -236,22 +223,7 @@ export class ForwarderTestFactory {
|
||||
.plus(expectedResults.maxOversoldWeth)
|
||||
.plus(ethSpentOnForwarderFee);
|
||||
|
||||
if (options.revertReason !== undefined) {
|
||||
await expectTransactionFailedAsync(
|
||||
this._forwarderWrapper.marketSellOrdersWithEthAsync(
|
||||
orders,
|
||||
{
|
||||
value: ethValue,
|
||||
from: this._takerAddress,
|
||||
},
|
||||
{ feePercentage, feeRecipient: this._forwarderFeeRecipientAddress },
|
||||
),
|
||||
options.revertReason,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const tx = await this._forwarderWrapper.marketSellOrdersWithEthAsync(
|
||||
const tx = this._forwarderWrapper.marketSellOrdersWithEthAsync(
|
||||
orders,
|
||||
{
|
||||
value: ethValue,
|
||||
@@ -260,10 +232,22 @@ export class ForwarderTestFactory {
|
||||
{ feePercentage, feeRecipient: this._forwarderFeeRecipientAddress },
|
||||
);
|
||||
|
||||
await this._checkResultsAsync(tx, expectedResults, takerEthBalanceBefore, erc20Balances, makerAssetContract, {
|
||||
forwarderFeePercentage,
|
||||
forwarderFeeRecipientEthBalanceBefore,
|
||||
});
|
||||
if (options.revertError !== undefined) {
|
||||
await expect(tx).to.revertWith(options.revertError);
|
||||
} else {
|
||||
const gasUsed = (await tx).gasUsed;
|
||||
await this._checkResultsAsync(
|
||||
gasUsed,
|
||||
expectedResults,
|
||||
takerEthBalanceBefore,
|
||||
erc20Balances,
|
||||
makerAssetContract,
|
||||
{
|
||||
forwarderFeePercentage,
|
||||
forwarderFeeRecipientEthBalanceBefore,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private _checkErc20Balances(
|
||||
@@ -297,7 +281,7 @@ export class ForwarderTestFactory {
|
||||
}
|
||||
|
||||
private async _checkResultsAsync(
|
||||
tx: TransactionReceiptWithDecodedLogs,
|
||||
gasUsed: number,
|
||||
expectedResults: ForwarderFillState,
|
||||
takerEthBalanceBefore: BigNumber,
|
||||
erc20Balances: ERC20BalancesByOwner,
|
||||
@@ -315,7 +299,7 @@ export class ForwarderTestFactory {
|
||||
const totalEthSpent = expectedResults.takerAssetFillAmount
|
||||
.plus(expectedResults.wethFees)
|
||||
.plus(ethSpentOnForwarderFee)
|
||||
.plus(this._gasPrice.times(tx.gasUsed));
|
||||
.plus(this._gasPrice.times(gasUsed));
|
||||
|
||||
const takerEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(this._takerAddress);
|
||||
const forwarderEthBalance = await web3Wrapper.getBalanceInWeiAsync(this._forwarderAddress);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { RevertReason } from '@0x/types';
|
||||
import { logUtils } from '@0x/utils';
|
||||
import { logUtils, RevertError } from '@0x/utils';
|
||||
import { NodeType } from '@0x/web3-wrapper';
|
||||
import * as chai from 'chai';
|
||||
import { TransactionReceipt, TransactionReceiptStatus, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
||||
@@ -90,7 +90,7 @@ export async function expectInsufficientFundsAsync<T>(p: Promise<T>): Promise<vo
|
||||
* @returns a new Promise which will reject if the conditions are not met and
|
||||
* otherwise resolve with no value.
|
||||
*/
|
||||
export async function expectTransactionFailedAsync(p: sendTransactionResult, reason: RevertReason): Promise<void> {
|
||||
export async function expectTransactionFailedAsync(p: sendTransactionResult, reason: RevertReason | RevertError): Promise<void> {
|
||||
// HACK(albrow): This dummy `catch` should not be necessary, but if you
|
||||
// remove it, there is an uncaught exception and the Node process will
|
||||
// forcibly exit. It's possible this is a false positive in
|
||||
@@ -184,7 +184,7 @@ export async function expectContractCallFailedWithoutReasonAsync<T>(p: Promise<T
|
||||
*/
|
||||
export async function expectContractCreationFailedAsync<T>(
|
||||
p: sendTransactionResult,
|
||||
reason: RevertReason,
|
||||
reason: RevertReason | RevertError,
|
||||
): Promise<void> {
|
||||
return expectTransactionFailedAsync(p, reason);
|
||||
}
|
||||
|
||||
105
packages/order-utils/src/forwarder_revert_errors.ts
Normal file
105
packages/order-utils/src/forwarder_revert_errors.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
import { BigNumber, RevertError } from '@0x/utils';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
// tslint:disable:max-classes-per-file
|
||||
|
||||
export class UnregisteredAssetProxyError extends RevertError {
|
||||
constructor() {
|
||||
super('UnregisteredAssetProxyError', 'UnregisteredAssetProxyError()', {});
|
||||
}
|
||||
}
|
||||
|
||||
export class UnsupportedAssetProxyError extends RevertError {
|
||||
constructor(proxyId?: string) {
|
||||
super('UnsupportedAssetProxyError', 'UnsupportedAssetProxyError(bytes4 proxyId)', { proxyId });
|
||||
}
|
||||
}
|
||||
|
||||
export class CompleteFillFailedError extends RevertError {
|
||||
constructor() {
|
||||
super('CompleteFillFailedError', 'CompleteFillFailedError()', {});
|
||||
}
|
||||
}
|
||||
|
||||
export class MakerAssetMismatchError extends RevertError {
|
||||
constructor(firstOrderMakerAssetData?: string, mismatchedMakerAssetData?: string) {
|
||||
super(
|
||||
'MakerAssetMismatchError',
|
||||
'MakerAssetMismatchError(bytes firstOrderMakerAssetData, bytes mismatchedMakerAssetData)',
|
||||
{
|
||||
firstOrderMakerAssetData,
|
||||
mismatchedMakerAssetData,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class FeePercentageTooLargeError extends RevertError {
|
||||
constructor(feePercentage?: BigNumber | number | string) {
|
||||
super('FeePercentageTooLargeError', 'FeePercentageTooLargeError(uint256 feePercentage)', {
|
||||
feePercentage,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class InsufficientEthForFeeError extends RevertError {
|
||||
constructor() {
|
||||
super('InsufficientEthForFeeError', 'InsufficientEthForFeeError()', {});
|
||||
}
|
||||
}
|
||||
|
||||
export class OversoldWethError extends RevertError {
|
||||
constructor(wethSold?: BigNumber | number | string, msgValue?: BigNumber | number | string) {
|
||||
super('OversoldWethError', 'OversoldWethError(uint256 wethSold, uint256 msgValue)', {
|
||||
wethSold,
|
||||
msgValue,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class TransferFailedError extends RevertError {
|
||||
constructor() {
|
||||
super('TransferFailedError', 'TransferFailedError()', {});
|
||||
}
|
||||
}
|
||||
|
||||
export class DefaultFunctionWethContractOnlyError extends RevertError {
|
||||
constructor(callerAddress?: string) {
|
||||
super('DefaultFunctionWethContractOnlyError', 'DefaultFunctionWethContractOnlyError(address callerAddress)', {
|
||||
callerAddress,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class InvalidMsgValueError extends RevertError {
|
||||
constructor() {
|
||||
super('InvalidMsgValueError', 'InvalidMsgValueError()', {});
|
||||
}
|
||||
}
|
||||
|
||||
export class InvalidErc721AmountError extends RevertError {
|
||||
constructor(amount?: BigNumber | number | string) {
|
||||
super('InvalidErc721AmountError', 'InvalidErc721AmountError(uint256 amount)', {
|
||||
amount,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const types = [
|
||||
UnregisteredAssetProxyError,
|
||||
UnsupportedAssetProxyError,
|
||||
CompleteFillFailedError,
|
||||
MakerAssetMismatchError,
|
||||
FeePercentageTooLargeError,
|
||||
InsufficientEthForFeeError,
|
||||
OversoldWethError,
|
||||
TransferFailedError,
|
||||
DefaultFunctionWethContractOnlyError,
|
||||
InvalidMsgValueError,
|
||||
InvalidErc721AmountError,
|
||||
];
|
||||
|
||||
// Register the types we've defined.
|
||||
for (const type of types) {
|
||||
RevertError.registerType(type);
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import * as ExchangeRevertErrors from './exchange_revert_errors';
|
||||
import * as ForwarderRevertErrors from './forwarder_revert_errors';
|
||||
import * as LibMathRevertErrors from './lib_math_revert_errors';
|
||||
|
||||
export { orderHashUtils } from './order_hash';
|
||||
@@ -85,4 +86,4 @@ export {
|
||||
} from './types';
|
||||
|
||||
export { ExchangeContract, NetworkId } from '@0x/abi-gen-wrappers';
|
||||
export { ExchangeRevertErrors, LibMathRevertErrors };
|
||||
export { ExchangeRevertErrors, ForwarderRevertErrors, LibMathRevertErrors };
|
||||
|
||||
Reference in New Issue
Block a user