From 49d223f3442901529b9e1d424186a56070f23aa5 Mon Sep 17 00:00:00 2001 From: Michael Zhu Date: Mon, 16 Sep 2019 10:25:41 -0700 Subject: [PATCH] MixinVaultCore unit tests --- .../contracts/test/TestMixinVaultCore.sol | 44 +++++++++ contracts/staking/package.json | 2 +- contracts/staking/src/artifacts.ts | 2 + contracts/staking/src/wrappers.ts | 1 + .../test/unit_tests/mixin_vault_core_test.ts | 99 +++++++++++++++++++ contracts/staking/test/utils/api_wrapper.ts | 16 +-- contracts/staking/tsconfig.json | 1 + 7 files changed, 156 insertions(+), 9 deletions(-) create mode 100644 contracts/staking/contracts/test/TestMixinVaultCore.sol create mode 100644 contracts/staking/test/unit_tests/mixin_vault_core_test.ts diff --git a/contracts/staking/contracts/test/TestMixinVaultCore.sol b/contracts/staking/contracts/test/TestMixinVaultCore.sol new file mode 100644 index 0000000000..56a4ccd6c1 --- /dev/null +++ b/contracts/staking/contracts/test/TestMixinVaultCore.sol @@ -0,0 +1,44 @@ +/* + + 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/vaults/MixinVaultCore.sol"; + + +// solhint-disable no-empty-blocks +contract TestMixinVaultCore is + MixinVaultCore +{ + function assertStakingProxy() + external + view + onlyStakingProxy + {} + + function assertInCatastrophicFailure() + external + view + onlyInCatastrophicFailure + {} + + function assertNotInCatastrophicFailure() + external + view + onlyNotInCatastrophicFailure + {} +} diff --git a/contracts/staking/package.json b/contracts/staking/package.json index 619ded1aaa..f380eec6e1 100644 --- a/contracts/staking/package.json +++ b/contracts/staking/package.json @@ -37,7 +37,7 @@ }, "config": { "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.", - "abis": "./generated-artifacts/@(EthVault|IEthVault|IStaking|IStakingEvents|IStakingPoolRewardVault|IStakingProxy|IStorage|IStorageInit|IStructs|IVaultCore|IZrxVault|LibCobbDouglas|LibFixedMath|LibFixedMathRichErrors|LibProxy|LibSafeDowncast|LibStakingRichErrors|MixinConstants|MixinCumulativeRewards|MixinDeploymentConstants|MixinExchangeFees|MixinExchangeManager|MixinParams|MixinScheduler|MixinStake|MixinStakeBalances|MixinStakeStorage|MixinStakingPool|MixinStakingPoolMakers|MixinStakingPoolModifiers|MixinStakingPoolRewards|MixinStorage|MixinVaultCore|ReadOnlyProxy|Staking|StakingPoolRewardVault|StakingProxy|TestCobbDouglas|TestCumulativeRewardTracking|TestInitTarget|TestLibFixedMath|TestLibProxy|TestLibProxyReceiver|TestLibSafeDowncast|TestProtocolFees|TestProtocolFeesERC20Proxy|TestStaking|TestStakingProxy|TestStorageLayout|ZrxVault).json" + "abis": "./generated-artifacts/@(EthVault|IEthVault|IStaking|IStakingEvents|IStakingPoolRewardVault|IStakingProxy|IStorage|IStorageInit|IStructs|IVaultCore|IZrxVault|LibCobbDouglas|LibFixedMath|LibFixedMathRichErrors|LibProxy|LibSafeDowncast|LibStakingRichErrors|MixinConstants|MixinCumulativeRewards|MixinDeploymentConstants|MixinExchangeFees|MixinExchangeManager|MixinParams|MixinScheduler|MixinStake|MixinStakeBalances|MixinStakeStorage|MixinStakingPool|MixinStakingPoolMakers|MixinStakingPoolModifiers|MixinStakingPoolRewards|MixinStorage|MixinVaultCore|ReadOnlyProxy|Staking|StakingPoolRewardVault|StakingProxy|TestCobbDouglas|TestCumulativeRewardTracking|TestInitTarget|TestLibFixedMath|TestLibProxy|TestLibProxyReceiver|TestLibSafeDowncast|TestMixinVaultCore|TestProtocolFees|TestProtocolFeesERC20Proxy|TestStaking|TestStakingProxy|TestStorageLayout|ZrxVault).json" }, "repository": { "type": "git", diff --git a/contracts/staking/src/artifacts.ts b/contracts/staking/src/artifacts.ts index cc2e78763c..27b72dabb5 100644 --- a/contracts/staking/src/artifacts.ts +++ b/contracts/staking/src/artifacts.ts @@ -49,6 +49,7 @@ import * as TestLibFixedMath from '../generated-artifacts/TestLibFixedMath.json' import * as TestLibProxy from '../generated-artifacts/TestLibProxy.json'; import * as TestLibProxyReceiver from '../generated-artifacts/TestLibProxyReceiver.json'; import * as TestLibSafeDowncast from '../generated-artifacts/TestLibSafeDowncast.json'; +import * as TestMixinVaultCore from '../generated-artifacts/TestMixinVaultCore.json'; import * as TestProtocolFees from '../generated-artifacts/TestProtocolFees.json'; import * as TestProtocolFeesERC20Proxy from '../generated-artifacts/TestProtocolFeesERC20Proxy.json'; import * as TestStaking from '../generated-artifacts/TestStaking.json'; @@ -101,6 +102,7 @@ export const artifacts = { TestLibProxy: TestLibProxy as ContractArtifact, TestLibProxyReceiver: TestLibProxyReceiver as ContractArtifact, TestLibSafeDowncast: TestLibSafeDowncast as ContractArtifact, + TestMixinVaultCore: TestMixinVaultCore as ContractArtifact, TestProtocolFees: TestProtocolFees as ContractArtifact, TestProtocolFeesERC20Proxy: TestProtocolFeesERC20Proxy as ContractArtifact, TestStaking: TestStaking as ContractArtifact, diff --git a/contracts/staking/src/wrappers.ts b/contracts/staking/src/wrappers.ts index 5bf6988fcd..5a4ecca61c 100644 --- a/contracts/staking/src/wrappers.ts +++ b/contracts/staking/src/wrappers.ts @@ -47,6 +47,7 @@ export * from '../generated-wrappers/test_lib_fixed_math'; export * from '../generated-wrappers/test_lib_proxy'; export * from '../generated-wrappers/test_lib_proxy_receiver'; export * from '../generated-wrappers/test_lib_safe_downcast'; +export * from '../generated-wrappers/test_mixin_vault_core'; export * from '../generated-wrappers/test_protocol_fees'; export * from '../generated-wrappers/test_protocol_fees_erc20_proxy'; export * from '../generated-wrappers/test_staking'; diff --git a/contracts/staking/test/unit_tests/mixin_vault_core_test.ts b/contracts/staking/test/unit_tests/mixin_vault_core_test.ts new file mode 100644 index 0000000000..f20aab2570 --- /dev/null +++ b/contracts/staking/test/unit_tests/mixin_vault_core_test.ts @@ -0,0 +1,99 @@ +import { blockchainTests, expect, filterLogsToArguments } from '@0x/contracts-test-utils'; +import { StakingRevertErrors } from '@0x/order-utils'; +import { OwnableRevertErrors } from '@0x/utils'; + +import { constants } from '../utils/constants'; + +import { + artifacts, + TestMixinVaultCoreContract, + TestMixinVaultCoreInCatastrophicFailureModeEventArgs, + TestMixinVaultCoreStakingProxySetEventArgs, +} from '../../src'; + +blockchainTests.resets('MixinVaultCore', env => { + let owner: string; + let nonOwnerAddresses: string[]; + let testContract: TestMixinVaultCoreContract; + + before(async () => { + [owner, ...nonOwnerAddresses] = await env.getAccountAddressesAsync(); + + testContract = await TestMixinVaultCoreContract.deployFrom0xArtifactAsync( + artifacts.TestMixinVaultCore, + env.provider, + env.txDefaults, + artifacts, + ); + }); + + describe('Set staking proxy', () => { + async function testAssertStakingProxyAsync(callerAddress: string): Promise { + const tx = testContract.assertStakingProxy.callAsync({ from: callerAddress }); + const expectedError = new StakingRevertErrors.OnlyCallableByStakingContractError(callerAddress); + expect(tx).to.revertWith(expectedError); + } + + it('Owner can set staking proxy', async () => { + const newAddress = nonOwnerAddresses[0]; + const receipt = await testContract.setStakingProxy.awaitTransactionSuccessAsync(newAddress, { + from: owner, + }); + const eventArgs = filterLogsToArguments( + receipt.logs, + 'StakingProxySet', + ); + expect(eventArgs.length).to.equal(1); + expect(eventArgs[0].stakingProxyAddress).to.equal(newAddress); + expect(await testContract.stakingProxyAddress.callAsync()).to.equal(newAddress); + // The new staking proxy address should be able to pass the modifier check + await testContract.assertStakingProxy.callAsync({ from: newAddress }); + return testAssertStakingProxyAsync(owner); + }); + it('Non-owner address cannot set staking proxy', async () => { + const notOwner = nonOwnerAddresses[0]; + const newAddress = nonOwnerAddresses[1]; + const tx = testContract.setStakingProxy.awaitTransactionSuccessAsync(newAddress, { + from: notOwner, + }); + const expectedError = new OwnableRevertErrors.OnlyOwnerError(notOwner); + expect(tx).to.revertWith(expectedError); + expect(await testContract.stakingProxyAddress.callAsync()).to.equal(constants.NIL_ADDRESS); + return testAssertStakingProxyAsync(newAddress); + }); + }); + + describe('Catastrophic failure mode', () => { + async function testCatastrophicFailureModeAsync(isInCatastrophicFailure: boolean): Promise { + const [expectToSucceed, expectToRevert] = isInCatastrophicFailure + ? [testContract.assertInCatastrophicFailure, testContract.assertNotInCatastrophicFailure] + : [testContract.assertNotInCatastrophicFailure, testContract.assertInCatastrophicFailure]; + const expectedError = isInCatastrophicFailure + ? new StakingRevertErrors.OnlyCallableIfNotInCatastrophicFailureError() + : new StakingRevertErrors.OnlyCallableIfInCatastrophicFailureError(); + await expectToSucceed.callAsync(); + expect(expectToRevert.callAsync()).to.revertWith(expectedError); + expect(await testContract.isInCatastrophicFailure.callAsync()).to.equal(isInCatastrophicFailure); + } + + it('Owner can turn on catastrophic failure mode', async () => { + await testCatastrophicFailureModeAsync(false); + const receipt = await testContract.enterCatastrophicFailure.awaitTransactionSuccessAsync({ from: owner }); + const eventArgs = filterLogsToArguments( + receipt.logs, + 'InCatastrophicFailureMode', + ); + expect(eventArgs.length).to.equal(1); + expect(eventArgs[0].sender).to.equal(owner); + return testCatastrophicFailureModeAsync(true); + }); + it('Non-owner cannot turn on catastrophic failure mode', async () => { + await testCatastrophicFailureModeAsync(false); + const tx = testContract.enterCatastrophicFailure.awaitTransactionSuccessAsync({ + from: nonOwnerAddresses[0], + }); + expect(tx).to.revertWith(new OwnableRevertErrors.OnlyOwnerError()); + return testCatastrophicFailureModeAsync(false); + }); + }); +}); diff --git a/contracts/staking/test/utils/api_wrapper.ts b/contracts/staking/test/utils/api_wrapper.ts index 941dac3a73..6979649ba7 100644 --- a/contracts/staking/test/utils/api_wrapper.ts +++ b/contracts/staking/test/utils/api_wrapper.ts @@ -1,6 +1,6 @@ import { ERC20Wrapper } from '@0x/contracts-asset-proxy'; import { artifacts as erc20Artifacts, DummyERC20TokenContract } from '@0x/contracts-erc20'; -import { BlockchainTestsEnvironment, constants, txDefaults } from '@0x/contracts-test-utils'; +import { BlockchainTestsEnvironment, constants } from '@0x/contracts-test-utils'; import { BigNumber, logUtils } from '@0x/utils'; import { Web3Wrapper } from '@0x/web3-wrapper'; import { ContractArtifact, TransactionReceiptWithDecodedLogs } from 'ethereum-types'; @@ -131,7 +131,7 @@ export class StakingApiWrapper { stakingProxyContract.address, env.provider, { - ...txDefaults, + ...env.txDefaults, from: ownerAddress, to: stakingProxyContract.address, gas: 3e6, @@ -162,35 +162,35 @@ export async function deployAndConfigureContractsAsync( const stakingContract = await StakingContract.deployFrom0xArtifactAsync( customStakingArtifact !== undefined ? customStakingArtifact : artifacts.Staking, env.provider, - txDefaults, + env.txDefaults, artifacts, ); // deploy read-only proxy const readOnlyProxyContract = await ReadOnlyProxyContract.deployFrom0xArtifactAsync( artifacts.ReadOnlyProxy, env.provider, - txDefaults, + env.txDefaults, artifacts, ); // deploy eth vault const ethVaultContract = await EthVaultContract.deployFrom0xArtifactAsync( artifacts.EthVault, env.provider, - txDefaults, + env.txDefaults, artifacts, ); // deploy reward vault const rewardVaultContract = await StakingPoolRewardVaultContract.deployFrom0xArtifactAsync( artifacts.StakingPoolRewardVault, env.provider, - txDefaults, + env.txDefaults, artifacts, ); // deploy zrx vault const zrxVaultContract = await ZrxVaultContract.deployFrom0xArtifactAsync( artifacts.ZrxVault, env.provider, - txDefaults, + env.txDefaults, artifacts, erc20ProxyContract.address, zrxTokenContract.address, @@ -199,7 +199,7 @@ export async function deployAndConfigureContractsAsync( const stakingProxyContract = await StakingProxyContract.deployFrom0xArtifactAsync( artifacts.StakingProxy, env.provider, - txDefaults, + env.txDefaults, artifacts, stakingContract.address, readOnlyProxyContract.address, diff --git a/contracts/staking/tsconfig.json b/contracts/staking/tsconfig.json index 03c2eddbcb..920fe86c67 100644 --- a/contracts/staking/tsconfig.json +++ b/contracts/staking/tsconfig.json @@ -47,6 +47,7 @@ "generated-artifacts/TestLibProxy.json", "generated-artifacts/TestLibProxyReceiver.json", "generated-artifacts/TestLibSafeDowncast.json", + "generated-artifacts/TestMixinVaultCore.json", "generated-artifacts/TestProtocolFees.json", "generated-artifacts/TestProtocolFeesERC20Proxy.json", "generated-artifacts/TestStaking.json",