Merge pull request #2014 from jalextowle/feature/contracts/3.0/utils-unit-tests

Added Unit Tests to Utils
This commit is contained in:
James Towle 2019-08-02 13:42:14 -07:00 committed by GitHub
commit bd42c33daa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 877 additions and 145 deletions

View File

@ -46,7 +46,7 @@ describe('Authorizable', () => {
await blockchainLifecycle.revertAsync();
});
describe('addAuthorizedAddress', () => {
it('should throw if not called by owner', async () => {
it('should revert if not called by owner', async () => {
const expectedError = new OwnableRevertErrors.OnlyOwnerError(notOwner, owner);
const tx = authorizable.addAuthorizedAddress.sendTransactionAsync(notOwner, { from: notOwner });
return expect(tx).to.revertWith(expectedError);
@ -60,7 +60,7 @@ describe('Authorizable', () => {
const isAuthorized = await authorizable.authorized.callAsync(address);
expect(isAuthorized).to.be.true();
});
it('should throw if owner attempts to authorize a duplicate address', async () => {
it('should revert if owner attempts to authorize a duplicate address', async () => {
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
address,
{ from: owner },
@ -74,7 +74,7 @@ describe('Authorizable', () => {
});
describe('removeAuthorizedAddress', () => {
it('should throw if not called by owner', async () => {
it('should revert if not called by owner', async () => {
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
address,
{ from: owner },
@ -100,7 +100,7 @@ describe('Authorizable', () => {
expect(isAuthorized).to.be.false();
});
it('should throw if owner attempts to remove an address that is not authorized', async () => {
it('should revert if owner attempts to remove an address that is not authorized', async () => {
return expectTransactionFailedAsync(
authorizable.removeAuthorizedAddress.sendTransactionAsync(address, {
from: owner,
@ -111,7 +111,7 @@ describe('Authorizable', () => {
});
describe('removeAuthorizedAddressAtIndex', () => {
it('should throw if not called by owner', async () => {
it('should revert if not called by owner', async () => {
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
address,
{ from: owner },
@ -124,7 +124,7 @@ describe('Authorizable', () => {
});
return expect(tx).to.revertWith(expectedError);
});
it('should throw if index is >= authorities.length', async () => {
it('should revert if index is >= authorities.length', async () => {
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(
address,
{ from: owner },
@ -138,7 +138,7 @@ describe('Authorizable', () => {
RevertReason.IndexOutOfBounds,
);
});
it('should throw if owner attempts to remove an address that is not authorized', async () => {
it('should revert if owner attempts to remove an address that is not authorized', async () => {
const index = new BigNumber(0);
return expectTransactionFailedAsync(
authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address, index, {
@ -147,7 +147,7 @@ describe('Authorizable', () => {
RevertReason.TargetNotAuthorized,
);
});
it('should throw if address at index does not match target', async () => {
it('should revert if address at index does not match target', async () => {
const address1 = address;
const address2 = notOwner;
await authorizable.addAuthorizedAddress.awaitTransactionSuccessAsync(

View File

@ -166,7 +166,7 @@ describe('ERC1155Token', () => {
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedFinalBalances);
});
it('should throw if transfer reverts', async () => {
it('should revert if transfer reverts', async () => {
// setup test parameters
const tokenToTransfer = fungibleToken;
const valueToTransfer = spenderInitialFungibleBalance.plus(1);
@ -187,7 +187,7 @@ describe('ERC1155Token', () => {
);
return expect(tx).to.revertWith(expectedError);
});
it('should throw if callback reverts', async () => {
it('should revert if callback reverts', async () => {
// setup test parameters
const tokenToTransfer = fungibleToken;
const valueToTransfer = fungibleValueToTransfer;
@ -342,7 +342,7 @@ describe('ERC1155Token', () => {
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
});
it('should throw if transfer reverts', async () => {
it('should revert if transfer reverts', async () => {
// setup test parameters
const tokensToTransfer = [fungibleToken];
const valuesToTransfer = [spenderInitialFungibleBalance.plus(1)];
@ -363,7 +363,7 @@ describe('ERC1155Token', () => {
);
return expect(tx).to.revertWith(expectedError);
});
it('should throw if callback reverts', async () => {
it('should revert if callback reverts', async () => {
// setup test parameters
const tokensToTransfer = [fungibleToken];
const valuesToTransfer = [fungibleValueToTransfer];
@ -417,7 +417,7 @@ describe('ERC1155Token', () => {
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, [tokenToTransfer], expectedFinalBalances);
});
it('should throw if trying to transfer tokens via safeTransferFrom by an unapproved account', async () => {
it('should revert if trying to transfer tokens via safeTransferFrom by an unapproved account', async () => {
// check approval not set
const isApprovedForAllCheck = await erc1155Wrapper.isApprovedForAllAsync(spender, delegatedSpender);
expect(isApprovedForAllCheck).to.be.false();
@ -470,7 +470,7 @@ describe('ERC1155Token', () => {
];
await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances);
});
it('should throw if trying to transfer tokens via safeBatchTransferFrom by an unapproved account', async () => {
it('should revert if trying to transfer tokens via safeBatchTransferFrom by an unapproved account', async () => {
// check approval not set
const isApprovedForAllCheck = await erc1155Wrapper.isApprovedForAllAsync(spender, delegatedSpender);
expect(isApprovedForAllCheck).to.be.false();

View File

@ -54,7 +54,7 @@ describe('UnlimitedAllowanceToken', () => {
await blockchainLifecycle.revertAsync();
});
describe('transfer', () => {
it('should throw if owner has insufficient balance', async () => {
it('should revert if owner has insufficient balance', async () => {
const ownerBalance = await token.balanceOf.callAsync(owner);
const amountToTransfer = ownerBalance.plus(1);
return expectContractCallFailedAsync(
@ -89,7 +89,7 @@ describe('UnlimitedAllowanceToken', () => {
});
describe('transferFrom', () => {
it('should throw if owner has insufficient balance', async () => {
it('should revert if owner has insufficient balance', async () => {
const ownerBalance = await token.balanceOf.callAsync(owner);
const amountToTransfer = ownerBalance.plus(1);
await web3Wrapper.awaitTransactionSuccessAsync(
@ -104,7 +104,7 @@ describe('UnlimitedAllowanceToken', () => {
);
});
it('should throw if spender has insufficient allowance', async () => {
it('should revert if spender has insufficient allowance', async () => {
const ownerBalance = await token.balanceOf.callAsync(owner);
const amountToTransfer = ownerBalance;

View File

@ -45,7 +45,7 @@ describe('EtherToken', () => {
await blockchainLifecycle.revertAsync();
});
describe('deposit', () => {
it('should throw if caller attempts to deposit more Ether than caller balance', async () => {
it('should revert if caller attempts to deposit more Ether than caller balance', async () => {
const initEthBalance = await web3Wrapper.getBalanceInWeiAsync(account);
const ethToDeposit = initEthBalance.plus(1);
@ -74,7 +74,7 @@ describe('EtherToken', () => {
});
describe('withdraw', () => {
it('should throw if caller attempts to withdraw greater than caller balance', async () => {
it('should revert if caller attempts to withdraw greater than caller balance', async () => {
const initEthTokenBalance = await etherToken.balanceOf.callAsync(account);
const ethTokensToWithdraw = initEthTokenBalance.plus(1);

View File

@ -17,7 +17,6 @@ pragma experimental ABIEncoderV2;
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
import "@0x/contracts-utils/contracts/src/ReentrancyGuard.sol";
import "@0x/contracts-utils/contracts/src/RichErrors.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibFillResults.sol";
import "./interfaces/IAssetProxyDispatcher.sol";

View File

@ -380,7 +380,7 @@ describe('Exchange core', () => {
});
});
it('should throw if fully filled', async () => {
it('should revert if fully filled', async () => {
signedOrder = await orderFactory.newSignedOrderAsync();
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress);
@ -423,7 +423,7 @@ describe('Exchange core', () => {
).to.be.bignumber.equal(signedOrder.takerFee);
});
it('should throw if order is expired', async () => {
it('should revert if order is expired', async () => {
const currentTimestamp = await getLatestBlockTimestampAsync();
signedOrder = await orderFactory.newSignedOrderAsync({
expirationTimeSeconds: new BigNumber(currentTimestamp).minus(10),
@ -742,14 +742,14 @@ describe('Exchange core', () => {
signedOrder = await orderFactory.newSignedOrderAsync();
});
it('should throw if not sent by maker', async () => {
it('should revert if not sent by maker', async () => {
const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
const expectedError = new ExchangeRevertErrors.InvalidMakerError(orderHash, takerAddress);
const tx = exchangeWrapper.cancelOrderAsync(signedOrder, takerAddress);
return expect(tx).to.revertWith(expectedError);
});
it('should throw if makerAssetAmount is 0', async () => {
it('should revert if makerAssetAmount is 0', async () => {
signedOrder = await orderFactory.newSignedOrderAsync({
makerAssetAmount: new BigNumber(0),
});
@ -762,7 +762,7 @@ describe('Exchange core', () => {
return expect(tx).to.revertWith(expectedError);
});
it('should throw if takerAssetAmount is 0', async () => {
it('should revert if takerAssetAmount is 0', async () => {
signedOrder = await orderFactory.newSignedOrderAsync({
takerAssetAmount: new BigNumber(0),
});
@ -800,7 +800,7 @@ describe('Exchange core', () => {
expect(orderHashUtils.getOrderHashHex(signedOrder)).to.be.equal(logArgs.orderHash);
});
it('should throw if already cancelled', async () => {
it('should revert if already cancelled', async () => {
await exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress);
const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
const expectedError = new ExchangeRevertErrors.OrderStatusError(orderHash, OrderStatus.Cancelled);
@ -808,7 +808,7 @@ describe('Exchange core', () => {
return expect(tx).to.revertWith(expectedError);
});
it('should throw if order is expired', async () => {
it('should revert if order is expired', async () => {
const currentTimestamp = await getLatestBlockTimestampAsync();
signedOrder = await orderFactory.newSignedOrderAsync({
expirationTimeSeconds: new BigNumber(currentTimestamp).minus(10),
@ -819,7 +819,7 @@ describe('Exchange core', () => {
return expect(tx).to.revertWith(expectedError);
});
it('should throw if rounding error is greater than 0.1%', async () => {
it('should revert if rounding error is greater than 0.1%', async () => {
signedOrder = await orderFactory.newSignedOrderAsync({
makerAssetAmount: new BigNumber(1001),
takerAssetAmount: new BigNumber(3),
@ -936,7 +936,7 @@ describe('Exchange core', () => {
});
describe('Testing Exchange of ERC721 Tokens', () => {
it('should throw when maker does not own the token with id makerAssetId', async () => {
it('should revert when maker does not own the token with id makerAssetId', async () => {
// Construct Exchange parameters
const makerAssetId = erc721TakerAssetIds[0];
const takerAssetId = erc721TakerAssetIds[1];
@ -963,7 +963,7 @@ describe('Exchange core', () => {
return expect(tx).to.revertWith(expectedError);
});
it('should throw when taker does not own the token with id takerAssetId', async () => {
it('should revert when taker does not own the token with id takerAssetId', async () => {
// Construct Exchange parameters
const makerAssetId = erc721MakerAssetIds[0];
const takerAssetId = erc721MakerAssetIds[1];
@ -990,7 +990,7 @@ describe('Exchange core', () => {
return expect(tx).to.revertWith(expectedError);
});
it('should throw when makerAssetAmount is greater than 1', async () => {
it('should revert when makerAssetAmount is greater than 1', async () => {
// Construct Exchange parameters
const makerAssetId = erc721MakerAssetIds[0];
const takerAssetId = erc721TakerAssetIds[0];
@ -1017,7 +1017,7 @@ describe('Exchange core', () => {
return expect(tx).to.revertWith(expectedError);
});
it('should throw when takerAssetAmount is greater than 1', async () => {
it('should revert when takerAssetAmount is greater than 1', async () => {
// Construct Exchange parameters
const makerAssetId = erc721MakerAssetIds[0];
const takerAssetId = erc721TakerAssetIds[0];
@ -1044,7 +1044,7 @@ describe('Exchange core', () => {
return expect(tx).to.revertWith(expectedError);
});
it('should throw on partial fill', async () => {
it('should revert on partial fill', async () => {
// Construct Exchange parameters
const makerAssetId = erc721MakerAssetIds[0];
signedOrder = await orderFactory.newSignedOrderAsync({

View File

@ -114,7 +114,7 @@ describe('AssetProxyDispatcher', () => {
expect(proxyAddress).to.be.equal(erc721Proxy.address);
});
it('should throw if a proxy with the same id is already registered', async () => {
it('should revert if a proxy with the same id is already registered', async () => {
// Initial registration
await assetProxyDispatcher.registerAssetProxy.awaitTransactionSuccessAsync(erc20Proxy.address, {
from: owner,
@ -134,7 +134,7 @@ describe('AssetProxyDispatcher', () => {
return expect(tx).to.revertWith(expectedError);
});
it('should throw if requesting address is not owner', async () => {
it('should revert if requesting address is not owner', async () => {
const expectedError = new OwnableRevertErrors.OnlyOwnerError(notOwner, owner);
const tx = assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(erc20Proxy.address, {
from: notOwner,
@ -250,7 +250,7 @@ describe('AssetProxyDispatcher', () => {
expect(newBalances).to.deep.equal(erc20Balances);
});
it('should throw if dispatching to unregistered proxy', async () => {
it('should revert if dispatching to unregistered proxy', async () => {
// Construct metadata for ERC20 proxy
const encodedAssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
// Perform a transfer from makerAddress to takerAddress

View File

@ -154,7 +154,7 @@ blockchainTests.resets('FillOrder Tests', ({ web3Wrapper, txDefaults }) => {
await fillOrderCombinatorialUtils.testFillOrderScenarioSuccessAsync(fillScenario);
});
it('should throw when taker is specified and order is claimed by other', async () => {
it('should revert when taker is specified and order is claimed by other', async () => {
const fillScenario = {
...defaultFillScenario,
orderScenario: {
@ -165,7 +165,7 @@ blockchainTests.resets('FillOrder Tests', ({ web3Wrapper, txDefaults }) => {
await fillOrderCombinatorialUtils.testFillOrderScenarioFailureAsync(fillScenario);
});
it('should throw if makerAssetAmount is 0', async () => {
it('should revert if makerAssetAmount is 0', async () => {
const fillScenario = {
...defaultFillScenario,
orderScenario: {
@ -177,7 +177,7 @@ blockchainTests.resets('FillOrder Tests', ({ web3Wrapper, txDefaults }) => {
await fillOrderCombinatorialUtils.testFillOrderScenarioFailureAsync(fillScenario);
});
it('should throw if takerAssetAmount is 0', async () => {
it('should revert if takerAssetAmount is 0', async () => {
const fillScenario = {
...defaultFillScenario,
orderScenario: {
@ -189,7 +189,7 @@ blockchainTests.resets('FillOrder Tests', ({ web3Wrapper, txDefaults }) => {
await fillOrderCombinatorialUtils.testFillOrderScenarioFailureAsync(fillScenario);
});
it('should throw if takerAssetFillAmount is 0', async () => {
it('should revert if takerAssetFillAmount is 0', async () => {
const fillScenario = {
...defaultFillScenario,
takerAssetFillAmountScenario: TakerAssetFillAmountScenario.Zero,
@ -197,7 +197,7 @@ blockchainTests.resets('FillOrder Tests', ({ web3Wrapper, txDefaults }) => {
await fillOrderCombinatorialUtils.testFillOrderScenarioFailureAsync(fillScenario);
});
it('should throw if an order is expired', async () => {
it('should revert if an order is expired', async () => {
const fillScenario = {
...defaultFillScenario,
orderScenario: {
@ -310,7 +310,7 @@ blockchainTests.resets('FillOrder Tests', ({ web3Wrapper, txDefaults }) => {
await fillOrderCombinatorialUtils.testFillOrderScenarioSuccessAsync(fillScenario);
});
it('should throw if maker balance is too low to fill order', async () => {
it('should revert if maker balance is too low to fill order', async () => {
const fillScenario = {
...defaultFillScenario,
makerStateScenario: {
@ -321,7 +321,7 @@ blockchainTests.resets('FillOrder Tests', ({ web3Wrapper, txDefaults }) => {
await fillOrderCombinatorialUtils.testFillOrderScenarioFailureAsync(fillScenario);
});
it('should throw if taker balance is too low to fill order', async () => {
it('should revert if taker balance is too low to fill order', async () => {
const fillScenario = {
...defaultFillScenario,
takerStateScenario: {
@ -332,7 +332,7 @@ blockchainTests.resets('FillOrder Tests', ({ web3Wrapper, txDefaults }) => {
await fillOrderCombinatorialUtils.testFillOrderScenarioFailureAsync(fillScenario);
});
it('should throw if maker allowances are too low to fill order', async () => {
it('should revert if maker allowances are too low to fill order', async () => {
const fillScenario = {
...defaultFillScenario,
makerStateScenario: {
@ -343,7 +343,7 @@ blockchainTests.resets('FillOrder Tests', ({ web3Wrapper, txDefaults }) => {
await fillOrderCombinatorialUtils.testFillOrderScenarioFailureAsync(fillScenario);
});
it('should throw if taker allowances are too low to fill order', async () => {
it('should revert if taker allowances are too low to fill order', async () => {
const fillScenario = {
...defaultFillScenario,
takerStateScenario: {
@ -354,7 +354,7 @@ blockchainTests.resets('FillOrder Tests', ({ web3Wrapper, txDefaults }) => {
await fillOrderCombinatorialUtils.testFillOrderScenarioFailureAsync(fillScenario);
});
it('should throw if maker fee balance is too low to fill order', async () => {
it('should revert if maker fee balance is too low to fill order', async () => {
const fillScenario = {
...defaultFillScenario,
makerStateScenario: {
@ -365,7 +365,7 @@ blockchainTests.resets('FillOrder Tests', ({ web3Wrapper, txDefaults }) => {
await fillOrderCombinatorialUtils.testFillOrderScenarioFailureAsync(fillScenario);
});
it('should throw if taker fee balance is too low to fill order', async () => {
it('should revert if taker fee balance is too low to fill order', async () => {
const fillScenario = {
...defaultFillScenario,
takerStateScenario: {
@ -376,7 +376,7 @@ blockchainTests.resets('FillOrder Tests', ({ web3Wrapper, txDefaults }) => {
await fillOrderCombinatorialUtils.testFillOrderScenarioFailureAsync(fillScenario);
});
it('should throw if maker fee allowances are too low to fill order', async () => {
it('should revert if maker fee allowances are too low to fill order', async () => {
const fillScenario = {
...defaultFillScenario,
makerStateScenario: {
@ -387,7 +387,7 @@ blockchainTests.resets('FillOrder Tests', ({ web3Wrapper, txDefaults }) => {
await fillOrderCombinatorialUtils.testFillOrderScenarioFailureAsync(fillScenario);
});
it('should throw if taker fee allowances are too low to fill order', async () => {
it('should revert if taker fee allowances are too low to fill order', async () => {
const fillScenario = {
...defaultFillScenario,
takerStateScenario: {

View File

@ -1286,7 +1286,7 @@ describe('matchOrders', () => {
);
});
it('Should throw if left order is not fillable', async () => {
it('Should revert if left order is not fillable', async () => {
// Create orders to match
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
@ -1305,7 +1305,7 @@ describe('matchOrders', () => {
return expect(tx).to.revertWith(expectedError);
});
it('Should throw if right order is not fillable', async () => {
it('Should revert if right order is not fillable', async () => {
// Create orders to match
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
@ -1324,7 +1324,7 @@ describe('matchOrders', () => {
return expect(tx).to.revertWith(expectedError);
});
it('should throw if there is not a positive spread', async () => {
it('should revert if there is not a positive spread', async () => {
// Create orders to match
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
@ -1342,7 +1342,7 @@ describe('matchOrders', () => {
return expect(tx).to.revertWith(expectedError);
});
it('should throw if the left maker asset is not equal to the right taker asset ', async () => {
it('should revert if the left maker asset is not equal to the right taker asset ', async () => {
// Create orders to match
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
@ -1373,7 +1373,7 @@ describe('matchOrders', () => {
return expect(tx).to.revertWith(expectedError);
});
it('should throw if the right maker asset is not equal to the left taker asset', async () => {
it('should revert if the right maker asset is not equal to the left taker asset', async () => {
// Create orders to match
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
@ -2416,7 +2416,7 @@ describe('matchOrders', () => {
);
});
it('Should throw if left order is not fillable', async () => {
it('Should revert if left order is not fillable', async () => {
// Create orders to match
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
@ -2435,7 +2435,7 @@ describe('matchOrders', () => {
return expect(tx).to.revertWith(expectedError);
});
it('Should throw if right order is not fillable', async () => {
it('Should revert if right order is not fillable', async () => {
// Create orders to match
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
@ -2454,7 +2454,7 @@ describe('matchOrders', () => {
return expect(tx).to.revertWith(expectedError);
});
it('should throw if there is not a positive spread', async () => {
it('should revert if there is not a positive spread', async () => {
// Create orders to match
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
@ -2472,7 +2472,7 @@ describe('matchOrders', () => {
return expect(tx).to.revertWith(expectedError);
});
it('should throw if the left maker asset is not equal to the right taker asset ', async () => {
it('should revert if the left maker asset is not equal to the right taker asset ', async () => {
// Create orders to match
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
@ -2503,7 +2503,7 @@ describe('matchOrders', () => {
return expect(tx).to.revertWith(expectedError);
});
it('should throw if the right maker asset is not equal to the left taker asset', async () => {
it('should revert if the right maker asset is not equal to the left taker asset', async () => {
// Create orders to match
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),

View File

@ -495,7 +495,7 @@ describe('MixinSignatureValidator', () => {
return expect(tx).to.revertWith(expectedError);
});
it('should throw when SignatureType=Validator, signature is valid and validator is not approved', async () => {
it('should revert when SignatureType=Validator, signature is valid and validator is not approved', async () => {
// Set approval of signature validator to false
await signatureValidator.setSignatureValidatorApproval.awaitTransactionSuccessAsync(
validatorWallet.address,
@ -702,7 +702,7 @@ describe('MixinSignatureValidator', () => {
return expect(tx).to.revertWith(expectedError);
});
it('should throw when SignatureType=Validator, signature is valid and validator is not approved', async () => {
it('should revert when SignatureType=Validator, signature is valid and validator is not approved', async () => {
// Set approval of signature validator to false
await signatureValidator.setSignatureValidatorApproval.awaitTransactionSuccessAsync(
validatorWallet.address,

View File

@ -234,7 +234,7 @@ describe('Exchange wrappers', () => {
);
});
it('should throw if a signedOrder is expired', async () => {
it('should revert if a signedOrder is expired', async () => {
const currentTimestamp = await getLatestBlockTimestampAsync();
const signedOrder = await orderFactory.newSignedOrderAsync({
expirationTimeSeconds: new BigNumber(currentTimestamp).minus(10),
@ -245,7 +245,7 @@ describe('Exchange wrappers', () => {
return expect(tx).to.revertWith(expectedError);
});
it('should throw if entire takerAssetFillAmount not filled', async () => {
it('should revert if entire takerAssetFillAmount not filled', async () => {
const signedOrder = await orderFactory.newSignedOrderAsync();
await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, {
@ -815,7 +815,7 @@ describe('Exchange wrappers', () => {
expect(newBalances).to.be.deep.equal(erc20Balances);
});
it('should throw if a single signedOrder does not fill the expected amount', async () => {
it('should revert if a single signedOrder does not fill the expected amount', async () => {
const takerAssetFillAmounts: BigNumber[] = [];
_.forEach(signedOrders, signedOrder => {
const takerAssetFillAmount = signedOrder.takerAssetAmount.div(2);
@ -922,7 +922,7 @@ describe('Exchange wrappers', () => {
expect(newBalances).to.be.deep.equal(erc20Balances);
});
it('should not throw if an order is invalid and fill the remaining orders', async () => {
it('should not revert if an order is invalid and fill the remaining orders', async () => {
const makerAssetAddress = erc20TokenA.address;
const takerAssetAddress = erc20TokenB.address;
@ -1132,7 +1132,7 @@ describe('Exchange wrappers', () => {
expect(newBalances).to.be.deep.equal(erc20Balances);
});
it('should throw when a signedOrder does not use the same takerAssetAddress', async () => {
it('should revert when a signedOrder does not use the same takerAssetAddress', async () => {
signedOrders = [
await orderFactory.newSignedOrderAsync(),
await orderFactory.newSignedOrderAsync({
@ -1510,7 +1510,7 @@ describe('Exchange wrappers', () => {
expect(newBalances).to.be.deep.equal(erc20Balances);
});
it('should throw when a signedOrder does not use the same makerAssetAddress', async () => {
it('should revert when a signedOrder does not use the same makerAssetAddress', async () => {
signedOrders = [
await orderFactory.newSignedOrderAsync(),
await orderFactory.newSignedOrderAsync({

View File

@ -115,7 +115,7 @@ describe('AssetProxyOwner', () => {
expect(isErc20ProxyRegistered).to.equal(true);
expect(isErc721ProxyRegistered).to.equal(true);
});
it('should throw if a null address is included in assetProxyContracts', async () => {
it('should revert if a null address is included in assetProxyContracts', async () => {
const assetProxyContractAddresses = [erc20Proxy.address, constants.NULL_ADDRESS];
return expectContractCreationFailedAsync(
(AssetProxyOwnerContract.deployFrom0xArtifactAsync(
@ -158,7 +158,7 @@ describe('AssetProxyOwner', () => {
});
describe('registerAssetProxy', () => {
it('should throw if not called by multisig', async () => {
it('should revert if not called by multisig', async () => {
const isRegistered = true;
return expectTransactionFailedWithoutReasonAsync(
testAssetProxyOwner.registerAssetProxy.sendTransactionAsync(erc20Proxy.address, isRegistered, {
@ -338,7 +338,7 @@ describe('AssetProxyOwner', () => {
});
describe('executeRemoveAuthorizedAddressAtIndex', () => {
it('should throw without the required confirmations', async () => {
it('should revert without the required confirmations', async () => {
const removeAuthorizedAddressAtIndexData = erc20Proxy.removeAuthorizedAddressAtIndex.getABIEncodedTransactionData(
authorized,
erc20Index,
@ -359,7 +359,7 @@ describe('AssetProxyOwner', () => {
);
});
it('should throw if tx destination is not registered', async () => {
it('should revert if tx destination is not registered', async () => {
const removeAuthorizedAddressAtIndexData = erc721Proxy.removeAuthorizedAddressAtIndex.getABIEncodedTransactionData(
authorized,
erc721Index,
@ -382,7 +382,7 @@ describe('AssetProxyOwner', () => {
);
});
it('should throw if tx data is not for removeAuthorizedAddressAtIndex', async () => {
it('should revert if tx data is not for removeAuthorizedAddressAtIndex', async () => {
const newAuthorized = owners[1];
const addAuthorizedAddressData = erc20Proxy.addAuthorizedAddress.getABIEncodedTransactionData(
newAuthorized,
@ -468,7 +468,7 @@ describe('AssetProxyOwner', () => {
expect(isAuthorizedAfter).to.equal(false);
});
it('should throw if already executed', async () => {
it('should revert if already executed', async () => {
const removeAuthorizedAddressAtIndexData = erc20Proxy.removeAuthorizedAddressAtIndex.getABIEncodedTransactionData(
authorized,
erc20Index,

View File

@ -241,13 +241,13 @@ describe('MultiSigWalletWithTimeLock', () => {
multiSigWrapper = new MultiSigWrapper(multiSig, provider);
});
it('should throw when not called by wallet', async () => {
it('should revert when not called by wallet', async () => {
return expectTransactionFailedWithoutReasonAsync(
multiSig.changeTimeLock.sendTransactionAsync(SECONDS_TIME_LOCKED, { from: owners[0] }),
);
});
it('should throw without enough confirmations', async () => {
it('should revert without enough confirmations', async () => {
const destination = multiSig.address;
const changeTimeLockData = multiSig.changeTimeLock.getABIEncodedTransactionData(SECONDS_TIME_LOCKED);
const res = await multiSigWrapper.submitTransactionAsync(destination, changeTimeLockData, owners[0]);
@ -325,7 +325,7 @@ describe('MultiSigWalletWithTimeLock', () => {
await multiSigWrapper.confirmTransactionAsync(txId, owners[1]);
});
it('should throw if it has enough confirmations but is not past the time lock', async () => {
it('should revert if it has enough confirmations but is not past the time lock', async () => {
return expectTransactionFailedAsync(
multiSig.executeTransaction.sendTransactionAsync(txId, { from: owners[0] }),
RevertReason.TimeLockIncomplete,

View File

@ -42,6 +42,7 @@ export const constants = {
NUM_ERC1155_FUNGIBLE_TOKENS_MINT: 4,
NUM_ERC1155_NONFUNGIBLE_TOKENS_MINT: 4,
NULL_ADDRESS: '0x0000000000000000000000000000000000000000',
NULL_BYTES32: '0x0000000000000000000000000000000000000000000000000000000000000000',
UNLIMITED_ALLOWANCE_IN_BASE_UNITS: new BigNumber(2).pow(256).minus(1),
TESTRPC_PRIVATE_KEYS: _.map(TESTRPC_PRIVATE_KEYS_STRINGS, privateKeyString => ethUtil.toBuffer(privateKeyString)),
INITIAL_ERC20_BALANCE: Web3Wrapper.toBaseUnitAmount(new BigNumber(10000), 18),

View File

@ -25,6 +25,10 @@
{
"note": "Updated RichErrors to the library pattern, and implemented RichErrors for all remaining reverts and requires",
"pr": 1913
},
{
"note": "Added unit tests for all of the internal functions in the package",
"pr": 2014
}
]
},

View File

@ -30,11 +30,16 @@
"src/LibEIP712.sol",
"src/Ownable.sol",
"src/ReentrancyGuard.sol",
"src/RichErrors.sol",
"src/SafeMath.sol",
"src/interfaces/IOwnable.sol",
"test/TestConstants.sol",
"test/TestLibAddress.sol",
"test/TestLibAddressArray.sol",
"test/TestLibBytes.sol"
"test/TestLibBytes.sol",
"test/TestLibEIP712.sol",
"test/TestLibRichErrors.sol",
"test/TestOwnable.sol",
"test/TestReentrancyGuard.sol",
"test/TestSafeMath.sol"
]
}

View File

@ -1,6 +1,6 @@
/*
Copyright 2018 ZeroEx Intl.
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -1,6 +1,6 @@
/*
Copyright 2018 ZeroEx Intl.
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -1,58 +0,0 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
contract RichErrors {
// bytes4(keccak256("Error(string)"))
bytes4 internal constant STANDARD_ERROR_SELECTOR =
0x08c379a0;
// solhint-disable func-name-mixedcase
/// @dev ABI encode a standard, string revert error payload.
/// This is the same payload that would be included by a `revert(string)`
/// solidity statement. It has the function signature `Error(string)`.
/// @param message The error string.
/// @return The ABI encoded error.
function StandardError(
string memory message
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
STANDARD_ERROR_SELECTOR,
bytes(message)
);
}
// solhint-enable func-name-mixedcase
/// @dev Reverts an encoded rich revert reason `errorData`.
/// @param errorData ABI encoded error data.
function _rrevert(bytes memory errorData)
internal
pure
{
assembly {
revert(add(errorData, 0x20), mload(errorData))
}
}
}

View File

@ -0,0 +1,35 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
import "../src/LibAddress.sol";
contract TestLibAddress {
using LibAddress for address;
function externalIsContract(address account)
external
view
returns (bool)
{
return account.isContract();
}
}

View File

@ -0,0 +1,52 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
import "../src/LibEIP712.sol";
contract TestLibEIP712 is
LibEIP712
{
function externalHashEIP712DomainSeperator(
string calldata name,
string calldata version,
uint256 chainid,
address verifyingcontractaddress
)
external
pure
returns (bytes32)
{
return _hashEIP712Domain(
name,
version,
chainid,
verifyingcontractaddress
);
}
function externalHashEIP712Message(bytes32 eip712DomainHash, bytes32 hashStruct)
external
pure
returns (bytes32)
{
return _hashEIP712Message(eip712DomainHash, hashStruct);
}
}

View File

@ -0,0 +1,32 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
import "../src/LibRichErrors.sol";
contract TestLibRichErrors {
function externalRRevert(bytes calldata errorData)
external
pure
{
LibRichErrors._rrevert(errorData);
}
}

View File

@ -0,0 +1,16 @@
pragma solidity ^0.5.9;
import "../src/Ownable.sol";
contract TestOwnable is
Ownable
{
function externalOnlyOwner()
external
onlyOwner
returns (bool)
{
return true;
}
}

View File

@ -0,0 +1,67 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
import "../src/ReentrancyGuard.sol";
contract TestReentrancyGuard is
ReentrancyGuard
{
uint256 internal counter = 2;
/// @dev Exposes the nonReentrant modifier publicly.
/// @param shouldBeAttacked True if the contract should be attacked.
/// @return True if successful.
function guarded(bool shouldBeAttacked)
external
nonReentrant
returns (bool)
{
if (shouldBeAttacked) {
return this.exploitive();
} else {
return this.nonExploitive();
}
}
/// @dev This is a function that will reenter the current contract.
/// @return True if successful.
function exploitive()
external
returns (bool)
{
if (counter > 0) {
counter--;
this.guarded(true);
} else {
counter = 2;
return true;
}
}
/// @dev This is a function that will not reenter the current contract.
/// @return True if successful.
function nonExploitive()
external
returns (bool)
{
return true;
}
}

View File

@ -0,0 +1,74 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
import "../src/SafeMath.sol";
contract TestSafeMath is
SafeMath
{
function externalSafeMul(uint256 a, uint256 b)
external
pure
returns (uint256)
{
return _safeMul(a, b);
}
function externalSafeDiv(uint256 a, uint256 b)
external
pure
returns (uint256)
{
return _safeDiv(a, b);
}
function externalSafeSub(uint256 a, uint256 b)
external
pure
returns (uint256)
{
return _safeSub(a, b);
}
function externalSafeAdd(uint256 a, uint256 b)
external
pure
returns (uint256)
{
return _safeAdd(a, b);
}
function externalMaxUint256(uint256 a, uint256 b)
external
pure
returns (uint256)
{
return _max256(a, b);
}
function externalMinUint256(uint256 a, uint256 b)
external
pure
returns (uint256)
{
return _min256(a, b);
}
}

View File

@ -34,7 +34,7 @@
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol"
},
"config": {
"abis": "./generated-artifacts/@(IOwnable|LibAddress|LibBytes|LibEIP712|Ownable|ReentrancyGuard|RichErrors|SafeMath|TestConstants|TestLibAddressArray|TestLibBytes).json",
"abis": "./generated-artifacts/@(IOwnable|LibAddress|LibBytes|LibEIP1271|LibEIP712|Ownable|ReentrancyGuard|SafeMath|TestConstants|TestLibAddress|TestLibAddressArray|TestLibBytes|TestLibEIP712|TestLibRichErrors|TestOwnable|TestReentrancyGuard|TestSafeMath).json",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
},
"repository": {

View File

@ -8,24 +8,36 @@ import { ContractArtifact } from 'ethereum-types';
import * as IOwnable from '../generated-artifacts/IOwnable.json';
import * as LibAddress from '../generated-artifacts/LibAddress.json';
import * as LibBytes from '../generated-artifacts/LibBytes.json';
import * as LibEIP1271 from '../generated-artifacts/LibEIP1271.json';
import * as LibEIP712 from '../generated-artifacts/LibEIP712.json';
import * as Ownable from '../generated-artifacts/Ownable.json';
import * as ReentrancyGuard from '../generated-artifacts/ReentrancyGuard.json';
import * as RichErrors from '../generated-artifacts/RichErrors.json';
import * as SafeMath from '../generated-artifacts/SafeMath.json';
import * as TestConstants from '../generated-artifacts/TestConstants.json';
import * as TestLibAddress from '../generated-artifacts/TestLibAddress.json';
import * as TestLibAddressArray from '../generated-artifacts/TestLibAddressArray.json';
import * as TestLibBytes from '../generated-artifacts/TestLibBytes.json';
import * as TestLibEIP712 from '../generated-artifacts/TestLibEIP712.json';
import * as TestLibRichErrors from '../generated-artifacts/TestLibRichErrors.json';
import * as TestOwnable from '../generated-artifacts/TestOwnable.json';
import * as TestReentrancyGuard from '../generated-artifacts/TestReentrancyGuard.json';
import * as TestSafeMath from '../generated-artifacts/TestSafeMath.json';
export const artifacts = {
LibAddress: LibAddress as ContractArtifact,
LibBytes: LibBytes as ContractArtifact,
LibEIP1271: LibEIP1271 as ContractArtifact,
LibEIP712: LibEIP712 as ContractArtifact,
Ownable: Ownable as ContractArtifact,
ReentrancyGuard: ReentrancyGuard as ContractArtifact,
RichErrors: RichErrors as ContractArtifact,
SafeMath: SafeMath as ContractArtifact,
IOwnable: IOwnable as ContractArtifact,
TestConstants: TestConstants as ContractArtifact,
TestLibAddress: TestLibAddress as ContractArtifact,
TestLibAddressArray: TestLibAddressArray as ContractArtifact,
TestLibBytes: TestLibBytes as ContractArtifact,
TestLibEIP712: TestLibEIP712 as ContractArtifact,
TestLibRichErrors: TestLibRichErrors as ContractArtifact,
TestOwnable: TestOwnable as ContractArtifact,
TestReentrancyGuard: TestReentrancyGuard as ContractArtifact,
TestSafeMath: TestSafeMath as ContractArtifact,
};

View File

@ -6,11 +6,17 @@
export * from '../generated-wrappers/i_ownable';
export * from '../generated-wrappers/lib_address';
export * from '../generated-wrappers/lib_bytes';
export * from '../generated-wrappers/lib_e_i_p1271';
export * from '../generated-wrappers/lib_e_i_p712';
export * from '../generated-wrappers/ownable';
export * from '../generated-wrappers/reentrancy_guard';
export * from '../generated-wrappers/rich_errors';
export * from '../generated-wrappers/safe_math';
export * from '../generated-wrappers/test_constants';
export * from '../generated-wrappers/test_lib_address';
export * from '../generated-wrappers/test_lib_address_array';
export * from '../generated-wrappers/test_lib_bytes';
export * from '../generated-wrappers/test_lib_e_i_p712';
export * from '../generated-wrappers/test_lib_rich_errors';
export * from '../generated-wrappers/test_ownable';
export * from '../generated-wrappers/test_reentrancy_guard';
export * from '../generated-wrappers/test_safe_math';

View File

@ -0,0 +1,37 @@
import { chaiSetup, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import * as chai from 'chai';
import { artifacts, TestLibAddressContract } from '../src';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
describe('LibAddress', () => {
let lib: TestLibAddressContract;
let nonContract: string;
before(async () => {
await blockchainLifecycle.startAsync();
nonContract = (await web3Wrapper.getAvailableAddressesAsync())[0];
// Deploy LibAddress
lib = await TestLibAddressContract.deployFrom0xArtifactAsync(artifacts.TestLibAddress, provider, txDefaults);
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
describe('isContract', () => {
it('should return false for a non-contract address', async () => {
const isContract = await lib.externalIsContract.callAsync(nonContract);
expect(isContract).to.be.false();
});
it('should return true for a non-contract address', async () => {
const isContract = await lib.externalIsContract.callAsync(lib.address);
expect(isContract).to.be.true();
});
});
});

View File

@ -0,0 +1,111 @@
import { chaiSetup, constants, hexConcat, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { BigNumber, signTypedDataUtils } from '@0x/utils';
import * as chai from 'chai';
import * as ethUtil from 'ethereumjs-util';
import * as _ from 'lodash';
import { artifacts, TestLibEIP712Contract } from '../src';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
/**
* Tests a specific instance of EIP712 domain hashing.
* @param lib The LibEIP712 contract to call.
* @param name The name of the domain.
* @param version The version of the domain.
* @param chainId The chain id of the domain.
* @param verifyingContractAddress The verifying contract address of the domain.
*/
async function testHashEIP712DomainAsync(
lib: TestLibEIP712Contract,
name: string,
version: string,
chainId: number,
verifyingContractAddress: string,
): Promise<void> {
const expectedHash = signTypedDataUtils.generateDomainHash({
name,
version,
chainId,
verifyingContractAddress,
});
const actualHash = await lib.externalHashEIP712DomainSeperator.callAsync(
name,
version,
new BigNumber(chainId),
verifyingContractAddress,
);
expect(actualHash).to.be.eq(hexConcat(expectedHash));
}
/**
* Tests a specific instance of EIP712 message hashing.
* @param lib The LibEIP712 contract to call.
* @param domainHash The hash of the EIP712 domain of this instance.
* @param hashStruct The hash of the struct of this instance.
*/
async function testHashEIP712MessageAsync(
lib: TestLibEIP712Contract,
domainHash: string,
hashStruct: string,
): Promise<void> {
const input = '0x1901'.concat(
domainHash.slice(2, domainHash.length).concat(hashStruct.slice(2, hashStruct.length)),
);
const expectedHash = '0x'.concat(ethUtil.sha3(input).toString('hex'));
const actualHash = await lib.externalHashEIP712Message.callAsync(domainHash, hashStruct);
expect(actualHash).to.be.eq(expectedHash);
}
describe('LibEIP712', () => {
let lib: TestLibEIP712Contract;
before(async () => {
await blockchainLifecycle.startAsync();
// Deploy LibEIP712
lib = await TestLibEIP712Contract.deployFrom0xArtifactAsync(artifacts.TestLibEIP712, provider, txDefaults);
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
describe('_hashEIP712Domain', async () => {
it('should correctly hash empty input', async () => {
await testHashEIP712DomainAsync(lib, '', '', 0, constants.NULL_ADDRESS);
});
it('should correctly hash non-empty input', async () => {
await testHashEIP712DomainAsync(lib, '_hashEIP712Domain', '1.0', 62, lib.address);
});
it('should correctly hash non-empty input', async () => {
await testHashEIP712DomainAsync(lib, '_hashEIP712Domain', '2.0', 0, lib.address);
});
});
describe('_hashEIP712Message', () => {
it('should correctly hash empty input', async () => {
await testHashEIP712MessageAsync(lib, constants.NULL_BYTES32, constants.NULL_BYTES32);
});
it('should correctly hash non-empty input', async () => {
await testHashEIP712MessageAsync(
lib,
'0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6', // keccak256(abi.encode(1))
'0x405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace', // keccak256(abi.encode(2))
);
});
it('should correctly hash non-empty input', async () => {
await testHashEIP712MessageAsync(
lib,
'0x405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace', // keccak256(abi.encode(2))
'0xc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b', // keccak256(abi.encode(3))
);
});
});
});

View File

@ -0,0 +1,40 @@
import { chaiSetup, constants, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { StringRevertError } from '@0x/utils';
import * as chai from 'chai';
import * as _ from 'lodash';
import { artifacts, TestLibRichErrorsContract } from '../src';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
describe('LibRichErrors', () => {
let lib: TestLibRichErrorsContract;
before(async () => {
await blockchainLifecycle.startAsync();
// Deploy SafeMath
lib = await TestLibRichErrorsContract.deployFrom0xArtifactAsync(
artifacts.TestLibRichErrors,
provider,
txDefaults,
);
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
describe('_rrevert', () => {
it('should correctly revert the extra bytes', async () => {
return expect(lib.externalRRevert.callAsync(constants.NULL_BYTES)).to.revertWith(constants.NULL_BYTES);
});
it('should correctly revert a StringRevertError', async () => {
const error = new StringRevertError('foo');
return expect(lib.externalRRevert.callAsync(error.encode())).to.revertWith(error);
});
});
});

View File

@ -0,0 +1,59 @@
import { chaiSetup, constants, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { OwnableRevertErrors } from '@0x/utils';
import * as chai from 'chai';
import * as _ from 'lodash';
import { artifacts, TestOwnableContract } from '../src';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
describe('Ownable', () => {
let ownable: TestOwnableContract;
let owner: string;
let nonOwner: string;
before(async () => {
const accounts = await web3Wrapper.getAvailableAddressesAsync();
owner = await accounts[0];
nonOwner = await accounts[1];
await blockchainLifecycle.startAsync();
// Deploy Ownable from the owner address
txDefaults.from = owner;
ownable = await TestOwnableContract.deployFrom0xArtifactAsync(artifacts.TestOwnable, provider, txDefaults);
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
describe('onlyOwner', () => {
it('should revert if sender is not the owner', async () => {
const expectedError = new OwnableRevertErrors.OnlyOwnerError(nonOwner, owner);
return expect(ownable.externalOnlyOwner.callAsync({ from: nonOwner })).to.revertWith(expectedError);
});
it('should succeed if sender is the owner', async () => {
const isSuccessful = await ownable.externalOnlyOwner.callAsync({ from: owner });
expect(isSuccessful).to.be.true();
});
});
describe('transferOwnership', () => {
it('should not transfer ownership if the specified new owner is the zero address', async () => {
expect(
ownable.transferOwnership.sendTransactionAsync(constants.NULL_ADDRESS, { from: owner }),
).to.be.fulfilled('');
const updatedOwner = await ownable.owner.callAsync();
expect(updatedOwner).to.be.eq(owner);
});
it('should transfer ownership if the specified new owner is not the zero address', async () => {
expect(ownable.transferOwnership.sendTransactionAsync(nonOwner, { from: owner })).to.be.fulfilled('');
const updatedOwner = await ownable.owner.callAsync();
expect(updatedOwner).to.be.eq(nonOwner);
});
});
});

View File

@ -0,0 +1,41 @@
import { chaiSetup, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { ReentrancyGuardRevertErrors } from '@0x/utils';
import * as chai from 'chai';
import * as _ from 'lodash';
import { artifacts, TestReentrancyGuardContract } from '../src';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
describe('ReentrancyGuard', () => {
let guard: TestReentrancyGuardContract;
before(async () => {
await blockchainLifecycle.startAsync();
// Deploy TestReentrancyGuard
guard = await TestReentrancyGuardContract.deployFrom0xArtifactAsync(
artifacts.TestReentrancyGuard,
provider,
txDefaults,
);
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
describe('nonReentrant', () => {
it('should revert if reentrancy occurs', async () => {
const expectedError = new ReentrancyGuardRevertErrors.IllegalReentrancyError();
return expect(guard.guarded.sendTransactionAsync(true)).to.revertWith(expectedError);
});
it('should succeed if reentrancy does not occur', async () => {
const isSuccessful = await guard.guarded.callAsync(false);
expect(isSuccessful).to.be.true();
});
});
});

View File

@ -0,0 +1,171 @@
import { chaiSetup, constants, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { BigNumber, SafeMathRevertErrors } from '@0x/utils';
import * as chai from 'chai';
import * as _ from 'lodash';
import { artifacts, TestSafeMathContract } from '../src';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
function toBigNumber(a: number | string): BigNumber {
return new BigNumber(a);
}
describe('SafeMath', () => {
let safeMath: TestSafeMathContract;
before(async () => {
await blockchainLifecycle.startAsync();
// Deploy SafeMath
safeMath = await TestSafeMathContract.deployFrom0xArtifactAsync(artifacts.TestSafeMath, provider, txDefaults);
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
describe('_safeMul', () => {
it('should return zero if first argument is zero', async () => {
const result = await safeMath.externalSafeMul.callAsync(constants.ZERO_AMOUNT, toBigNumber(1));
expect(result).bignumber.to.be.eq(constants.ZERO_AMOUNT);
});
it('should return zero if second argument is zero', async () => {
const result = await safeMath.externalSafeMul.callAsync(toBigNumber(1), constants.ZERO_AMOUNT);
expect(result).bignumber.to.be.eq(constants.ZERO_AMOUNT);
});
it('should revert if the multiplication overflows', async () => {
const a = toBigNumber('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'); // The largest uint256 number
const b = toBigNumber(2);
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow,
a,
b,
);
return expect(safeMath.externalSafeMul.callAsync(a, b)).to.revertWith(expectedError);
});
it("should calculate correct value for values that don't overflow", async () => {
const result = await safeMath.externalSafeMul.callAsync(toBigNumber(15), toBigNumber(13));
expect(result).bignumber.to.be.eq(toBigNumber(195));
});
});
describe('_safeDiv', () => {
it('should return the correct value if both values are the same', async () => {
const result = await safeMath.externalSafeDiv.callAsync(toBigNumber(1), toBigNumber(1));
expect(result).bignumber.to.be.eq(toBigNumber(1));
});
it('should return the correct value if the values are different', async () => {
const result = await safeMath.externalSafeDiv.callAsync(toBigNumber(3), toBigNumber(2));
expect(result).bignumber.to.be.eq(toBigNumber(1));
});
it('should return zero if the numerator is smaller than the denominator', async () => {
const result = await safeMath.externalSafeDiv.callAsync(toBigNumber(2), toBigNumber(3));
expect(result).bignumber.to.be.eq(constants.ZERO_AMOUNT);
});
it('should return zero if first argument is zero', async () => {
const result = await safeMath.externalSafeDiv.callAsync(constants.ZERO_AMOUNT, toBigNumber(1));
expect(result).bignumber.to.be.eq(constants.ZERO_AMOUNT);
});
it('should revert if second argument is zero', async () => {
const errMessage = 'VM Exception while processing transaction: invalid opcode';
return expect(safeMath.externalSafeDiv.callAsync(toBigNumber(1), constants.ZERO_AMOUNT)).to.be.rejectedWith(
errMessage,
);
});
});
describe('_safeSub', () => {
it('should revert if the subtraction underflows', async () => {
const a = toBigNumber(0);
const b = toBigNumber(1);
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256SubtractionUnderflow,
a,
b,
);
return expect(safeMath.externalSafeSub.callAsync(a, b)).to.revertWith(expectedError);
});
it('should calculate correct value for values that are equal', async () => {
const result = await safeMath.externalSafeMul.callAsync(constants.ZERO_AMOUNT, constants.ZERO_AMOUNT);
expect(result).bignumber.to.be.eq(constants.ZERO_AMOUNT);
});
it('should calculate correct value for values that are not equal', async () => {
const result = await safeMath.externalSafeSub.callAsync(toBigNumber(15), toBigNumber(13));
expect(result).bignumber.to.be.eq(toBigNumber(2));
});
});
describe('_safeAdd', () => {
it('should revert if the addition overflows', async () => {
const a = toBigNumber('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'); // The largest uint256 number
const b = toBigNumber(1);
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256AdditionOverflow,
a,
b,
);
return expect(safeMath.externalSafeAdd.callAsync(a, b)).to.revertWith(expectedError);
});
it('should calculate correct value if addition does not overflow', async () => {
const result = await safeMath.externalSafeAdd.callAsync(toBigNumber(15), toBigNumber(13));
expect(result).bignumber.to.be.eq(toBigNumber(28));
});
it('should calculate correct value if first argument is zero', async () => {
const result = await safeMath.externalSafeAdd.callAsync(constants.ZERO_AMOUNT, toBigNumber(13));
expect(result).bignumber.to.be.eq(toBigNumber(13));
});
it('should calculate correct value if second argument is zero', async () => {
const result = await safeMath.externalSafeAdd.callAsync(toBigNumber(13), constants.ZERO_AMOUNT);
expect(result).bignumber.to.be.eq(toBigNumber(13));
});
});
describe('_maxUint256', () => {
it('should return first argument if it is greater than the second', async () => {
const result = await safeMath.externalMaxUint256.callAsync(toBigNumber(13), constants.ZERO_AMOUNT);
expect(result).bignumber.to.be.eq(toBigNumber(13));
});
it('should return first argument if it is equal the second', async () => {
const result = await safeMath.externalMaxUint256.callAsync(constants.ZERO_AMOUNT, constants.ZERO_AMOUNT);
expect(result).bignumber.to.be.eq(constants.ZERO_AMOUNT);
});
it('should return second argument if it is greater than the first', async () => {
const result = await safeMath.externalMaxUint256.callAsync(constants.ZERO_AMOUNT, toBigNumber(13));
expect(result).bignumber.to.be.eq(toBigNumber(13));
});
});
describe('_minUint256', () => {
it('should return first argument if it is less than the second', async () => {
const result = await safeMath.externalMaxUint256.callAsync(constants.ZERO_AMOUNT, toBigNumber(13));
expect(result).bignumber.to.be.eq(toBigNumber(13));
});
it('should return first argument if it is equal the second', async () => {
const result = await safeMath.externalMaxUint256.callAsync(constants.ZERO_AMOUNT, constants.ZERO_AMOUNT);
expect(result).bignumber.to.be.eq(constants.ZERO_AMOUNT);
});
it('should return second argument if it is less than the first', async () => {
const result = await safeMath.externalMaxUint256.callAsync(toBigNumber(13), constants.ZERO_AMOUNT);
expect(result).bignumber.to.be.eq(toBigNumber(13));
});
});
});

View File

@ -6,14 +6,20 @@
"generated-artifacts/IOwnable.json",
"generated-artifacts/LibAddress.json",
"generated-artifacts/LibBytes.json",
"generated-artifacts/LibEIP1271.json",
"generated-artifacts/LibEIP712.json",
"generated-artifacts/Ownable.json",
"generated-artifacts/ReentrancyGuard.json",
"generated-artifacts/RichErrors.json",
"generated-artifacts/SafeMath.json",
"generated-artifacts/TestConstants.json",
"generated-artifacts/TestLibAddress.json",
"generated-artifacts/TestLibAddressArray.json",
"generated-artifacts/TestLibBytes.json"
"generated-artifacts/TestLibBytes.json",
"generated-artifacts/TestLibEIP712.json",
"generated-artifacts/TestLibRichErrors.json",
"generated-artifacts/TestOwnable.json",
"generated-artifacts/TestReentrancyGuard.json",
"generated-artifacts/TestSafeMath.json"
],
"exclude": ["./deploy/solc/solc_bin"]
}

View File

@ -10,7 +10,7 @@ export enum SafeMathErrorCodes {
}
export class SafeMathError extends RevertError {
constructor(error?: SafeMathErrorCodes, a?: BigNumber | number | string, b?: BigNumber | number | string) {
constructor(error?: SafeMathErrorCodes, a?: BigNumber, b?: BigNumber) {
super('SafeMathError', 'SafeMathError(uint8 error, uint256 a, uint256 b)', {
error,
a,

View File

@ -20,6 +20,28 @@ export const signTypedDataUtils = {
]),
);
},
/**
* Generates the hash of a EIP712 Domain with the default schema
* @param domain An EIP712 domain with the default schema containing a name, version, chain id,
* and verifying address.
* @return A buffer that contains the hash of the domain.
*/
generateDomainHash(domain: EIP712Object): Buffer {
return signTypedDataUtils._structHash(
'EIP712Domain',
domain,
// HACK(jalextowle): When we consolidate our testing packages into test-utils, we can use a constant
// to eliminate code duplication. At the moment, there isn't a good way to do that because of cyclic-dependencies.
{
EIP712Domain: [
{ name: 'name', type: 'string' },
{ name: 'version', type: 'string' },
{ name: 'chainId', type: 'uint256' },
{ name: 'verifyingContractAddress', type: 'address' },
],
},
);
},
_findDependencies(primaryType: string, types: EIP712Types, found: string[] = []): string[] {
if (found.includes(primaryType) || types[primaryType] === undefined) {
return found;