* move orderParsingUtils from order-utils to connect * Remove many functions from signatureUtils Removed from the exported object, that is. All of them are used in other existing code, so they were all moved to be as local to their usage as possible. * remove orderHashUtils.isValidOrderHash() * Move all *RevertErrors from order-utils... ...into their respective @0x/contracts- packages. * Refactor @0x/order-utils' orderHashUtils away - Move existing routines into @0x/contracts-test-utils - Migrate non-contract-test callers to a newly-exposed getOrderHash() method in DevUtils. * Move all *RevertErrors from @0x/utils... ...into their respective @0x/contracts- packages. * rm transactionHashUtils.isValidTransactionHash() * DevUtils.sol: Fail yarn test if too big to deploy * Refactor @0x/order-utils transactionHashUtils away - Move existing routines into @0x/contracts-test-utils - Migrate non-contract-test callers to a newly-exposed getTransactionHash() method in DevUtils. * Consolidate `Removed export...` CHANGELOG entries * Rm EthBalanceChecker from devutils wrapper exports * Stop importing from '.' or '.../src' * fix builds * fix prettier; dangling promise * increase max bundle size
302 lines
15 KiB
TypeScript
302 lines
15 KiB
TypeScript
import { blockchainTests, constants, expect, verifyEventsFromLogs } from '@0x/contracts-test-utils';
|
|
import { AuthorizableRevertErrors } from '@0x/contracts-utils';
|
|
import { BigNumber } from '@0x/utils';
|
|
import * as _ from 'lodash';
|
|
|
|
import { artifacts } from '../artifacts';
|
|
import {
|
|
StakingProxyEvents,
|
|
TestProxyDestinationContract,
|
|
TestProxyDestinationEvents,
|
|
TestStakingProxyUnitContract,
|
|
} from '../wrappers';
|
|
|
|
import { constants as stakingConstants } from '../../src/constants';
|
|
import StakingRevertErrors = require('../../src/staking_revert_errors');
|
|
|
|
blockchainTests.resets('StakingProxy unit tests', env => {
|
|
const testString = 'Hello, World!';
|
|
const testRevertString = 'Goodbye, World!';
|
|
let accounts: string[];
|
|
let owner: string;
|
|
let authorizedAddress: string;
|
|
let notAuthorizedAddresses: string[];
|
|
let testProxyContract: TestStakingProxyUnitContract;
|
|
let testContractViaProxy: TestProxyDestinationContract;
|
|
let testContract: TestProxyDestinationContract;
|
|
let testContract2: TestProxyDestinationContract;
|
|
|
|
before(async () => {
|
|
// Create accounts
|
|
accounts = await env.getAccountAddressesAsync();
|
|
[owner, authorizedAddress, ...notAuthorizedAddresses] = accounts;
|
|
|
|
// Deploy contracts
|
|
testContract = await TestProxyDestinationContract.deployFrom0xArtifactAsync(
|
|
artifacts.TestProxyDestination,
|
|
env.provider,
|
|
env.txDefaults,
|
|
artifacts,
|
|
);
|
|
testContract2 = await TestProxyDestinationContract.deployFrom0xArtifactAsync(
|
|
artifacts.TestProxyDestination,
|
|
env.provider,
|
|
env.txDefaults,
|
|
artifacts,
|
|
);
|
|
testProxyContract = await TestStakingProxyUnitContract.deployFrom0xArtifactAsync(
|
|
artifacts.TestStakingProxyUnit,
|
|
env.provider,
|
|
env.txDefaults,
|
|
artifacts,
|
|
testContract.address,
|
|
);
|
|
const logDecoderDependencies = _.mapValues(artifacts, v => v.compilerOutput.abi);
|
|
testContractViaProxy = new TestProxyDestinationContract(
|
|
testProxyContract.address,
|
|
env.provider,
|
|
env.txDefaults,
|
|
logDecoderDependencies,
|
|
);
|
|
|
|
// Add authorized address to Staking Proxy
|
|
await testProxyContract.addAuthorizedAddress(authorizedAddress).sendTransactionAsync({ from: owner });
|
|
});
|
|
|
|
describe('Fallback function', () => {
|
|
it('should pass back the return value of the destination contract', async () => {
|
|
const returnValue = await testContractViaProxy.echo(testString).callAsync();
|
|
expect(returnValue).to.equal(testString);
|
|
});
|
|
|
|
it('should revert with correct value when destination reverts', async () => {
|
|
return expect(testContractViaProxy.die().callAsync()).to.revertWith(testRevertString);
|
|
});
|
|
|
|
it('should revert if no staking contract is attached', async () => {
|
|
await testProxyContract.detachStakingContract().awaitTransactionSuccessAsync({ from: authorizedAddress });
|
|
const expectedError = new StakingRevertErrors.ProxyDestinationCannotBeNilError();
|
|
const tx = testContractViaProxy.echo(testString).callAsync();
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
});
|
|
|
|
describe('attachStakingContract', () => {
|
|
it('should successfully attaching a new staking contract', async () => {
|
|
// Cache existing staking contract and attach a new one
|
|
const initStakingContractAddress = await testProxyContract.stakingContract().callAsync();
|
|
const txReceipt = await testProxyContract
|
|
.attachStakingContract(testContract2.address)
|
|
.awaitTransactionSuccessAsync({ from: authorizedAddress });
|
|
|
|
// Validate `ContractAttachedToProxy` event
|
|
verifyEventsFromLogs(
|
|
txReceipt.logs,
|
|
[
|
|
{
|
|
newStakingContractAddress: testContract2.address,
|
|
},
|
|
],
|
|
StakingProxyEvents.StakingContractAttachedToProxy,
|
|
);
|
|
|
|
// Check that `init` was called on destination contract
|
|
verifyEventsFromLogs(
|
|
txReceipt.logs,
|
|
[
|
|
{
|
|
initCalled: true,
|
|
},
|
|
],
|
|
TestProxyDestinationEvents.InitCalled,
|
|
);
|
|
|
|
// Validate new staking contract address
|
|
const finalStakingContractAddress = await testProxyContract.stakingContract().callAsync();
|
|
expect(finalStakingContractAddress).to.be.equal(testContract2.address);
|
|
expect(finalStakingContractAddress).to.not.equal(initStakingContractAddress);
|
|
});
|
|
|
|
it('should revert if call to `init` on new staking contract fails', async () => {
|
|
await testProxyContract.setInitFailFlag().awaitTransactionSuccessAsync();
|
|
const tx = testProxyContract.attachStakingContract(testContract2.address).awaitTransactionSuccessAsync({
|
|
from: authorizedAddress,
|
|
});
|
|
const expectedError = 'INIT_FAIL_FLAG_SET';
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
|
|
it('should revert if called by unauthorized address', async () => {
|
|
const tx = testProxyContract.attachStakingContract(testContract2.address).awaitTransactionSuccessAsync({
|
|
from: notAuthorizedAddresses[0],
|
|
});
|
|
const expectedError = new AuthorizableRevertErrors.SenderNotAuthorizedError(notAuthorizedAddresses[0]);
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
});
|
|
|
|
describe('detachStakingContract', () => {
|
|
it('should detach staking contract', async () => {
|
|
// Cache existing staking contract and attach a new one
|
|
const initStakingContractAddress = await testProxyContract.stakingContract().callAsync();
|
|
const txReceipt = await testProxyContract.detachStakingContract().awaitTransactionSuccessAsync({
|
|
from: authorizedAddress,
|
|
});
|
|
|
|
// Validate that event was emitted
|
|
verifyEventsFromLogs(txReceipt.logs, [{}], StakingProxyEvents.StakingContractDetachedFromProxy);
|
|
|
|
// Validate staking contract address was unset
|
|
const finalStakingContractAddress = await testProxyContract.stakingContract().callAsync();
|
|
expect(finalStakingContractAddress).to.be.equal(stakingConstants.NIL_ADDRESS);
|
|
expect(finalStakingContractAddress).to.not.equal(initStakingContractAddress);
|
|
});
|
|
|
|
it('should revert if called by unauthorized address', async () => {
|
|
const tx = testProxyContract.detachStakingContract().awaitTransactionSuccessAsync({
|
|
from: notAuthorizedAddresses[0],
|
|
});
|
|
const expectedError = new AuthorizableRevertErrors.SenderNotAuthorizedError(notAuthorizedAddresses[0]);
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
});
|
|
|
|
describe('batchExecute', () => {
|
|
it('should execute no-op if no calls to make', async () => {
|
|
await testProxyContract.batchExecute([]).awaitTransactionSuccessAsync();
|
|
});
|
|
|
|
it('should call one function and return the output', async () => {
|
|
const calls = [testContract.echo(testString).getABIEncodedTransactionData()];
|
|
const rawResults = await testProxyContract.batchExecute(calls).callAsync();
|
|
expect(rawResults.length).to.equal(1);
|
|
const returnValues = [testContract.getABIDecodedReturnData<{}>('echo', rawResults[0])];
|
|
expect(returnValues[0]).to.equal(testString);
|
|
});
|
|
|
|
it('should call multiple functions and return their outputs', async () => {
|
|
const calls = [
|
|
testContract.echo(testString).getABIEncodedTransactionData(),
|
|
testContract.doMath(new BigNumber(2), new BigNumber(1)).getABIEncodedTransactionData(),
|
|
];
|
|
const rawResults = await testProxyContract.batchExecute(calls).callAsync();
|
|
expect(rawResults.length).to.equal(2);
|
|
const returnValues = [
|
|
testContract.getABIDecodedReturnData<string>('echo', rawResults[0]),
|
|
testContract.getABIDecodedReturnData<BigNumber[]>('doMath', rawResults[1]),
|
|
];
|
|
expect(returnValues[0]).to.equal(testString);
|
|
expect(returnValues[1][0]).to.bignumber.equal(new BigNumber(3));
|
|
expect(returnValues[1][1]).to.bignumber.equal(new BigNumber(1));
|
|
});
|
|
|
|
it('should revert if a call reverts', async () => {
|
|
const calls = [
|
|
testContract.echo(testString).getABIEncodedTransactionData(),
|
|
testContract.die().getABIEncodedTransactionData(),
|
|
testContract.doMath(new BigNumber(2), new BigNumber(1)).getABIEncodedTransactionData(),
|
|
];
|
|
const tx = testProxyContract.batchExecute(calls).callAsync();
|
|
const expectedError = testRevertString;
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
|
|
it('should revert if no staking contract is attached', async () => {
|
|
await testProxyContract.detachStakingContract().awaitTransactionSuccessAsync({ from: authorizedAddress });
|
|
const calls = [testContract.echo(testString).getABIEncodedTransactionData()];
|
|
|
|
const tx = testProxyContract.batchExecute(calls).callAsync();
|
|
const expectedError = new StakingRevertErrors.ProxyDestinationCannotBeNilError();
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
});
|
|
|
|
describe('assertValidStorageParams', () => {
|
|
const validStorageParams = {
|
|
epochDurationInSeconds: new BigNumber(stakingConstants.ONE_DAY_IN_SECONDS * 5),
|
|
cobbDouglasAlphaNumerator: new BigNumber(1),
|
|
cobbDouglasAlphaDenominator: new BigNumber(1),
|
|
rewardDelegatedStakeWeight: constants.PPM_DENOMINATOR,
|
|
minimumPoolStake: new BigNumber(100),
|
|
};
|
|
it('should not revert if all storage params are valid', async () => {
|
|
await testProxyContract.setTestStorageParams(validStorageParams).awaitTransactionSuccessAsync();
|
|
await testProxyContract.assertValidStorageParams().callAsync();
|
|
});
|
|
it('should revert if `epochDurationInSeconds` is less than 5 days', async () => {
|
|
const invalidStorageParams = {
|
|
...validStorageParams,
|
|
epochDurationInSeconds: new BigNumber(0),
|
|
};
|
|
await testProxyContract.setTestStorageParams(invalidStorageParams).awaitTransactionSuccessAsync();
|
|
const tx = testProxyContract.assertValidStorageParams().callAsync();
|
|
const expectedError = new StakingRevertErrors.InvalidParamValueError(
|
|
StakingRevertErrors.InvalidParamValueErrorCodes.InvalidEpochDuration,
|
|
);
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
it('should revert if `epochDurationInSeconds` is greater than 30 days', async () => {
|
|
const invalidStorageParams = {
|
|
...validStorageParams,
|
|
epochDurationInSeconds: new BigNumber(stakingConstants.ONE_DAY_IN_SECONDS * 31),
|
|
};
|
|
await testProxyContract.setTestStorageParams(invalidStorageParams).awaitTransactionSuccessAsync();
|
|
const tx = testProxyContract.assertValidStorageParams().callAsync();
|
|
const expectedError = new StakingRevertErrors.InvalidParamValueError(
|
|
StakingRevertErrors.InvalidParamValueErrorCodes.InvalidEpochDuration,
|
|
);
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
it('should revert if `cobbDouglasAlphaNumerator` is greater than `cobbDouglasAlphaDenominator`', async () => {
|
|
const invalidStorageParams = {
|
|
...validStorageParams,
|
|
cobbDouglasAlphaNumerator: new BigNumber(2),
|
|
cobbDouglasAlphaDenominator: new BigNumber(1),
|
|
};
|
|
await testProxyContract.setTestStorageParams(invalidStorageParams).awaitTransactionSuccessAsync();
|
|
const tx = testProxyContract.assertValidStorageParams().callAsync();
|
|
const expectedError = new StakingRevertErrors.InvalidParamValueError(
|
|
StakingRevertErrors.InvalidParamValueErrorCodes.InvalidCobbDouglasAlpha,
|
|
);
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
it('should revert if `cobbDouglasAlphaDenominator` equals zero', async () => {
|
|
const invalidStorageParams = {
|
|
...validStorageParams,
|
|
cobbDouglasAlphaDenominator: new BigNumber(0),
|
|
};
|
|
await testProxyContract.setTestStorageParams(invalidStorageParams).awaitTransactionSuccessAsync();
|
|
const tx = testProxyContract.assertValidStorageParams().callAsync();
|
|
const expectedError = new StakingRevertErrors.InvalidParamValueError(
|
|
StakingRevertErrors.InvalidParamValueErrorCodes.InvalidCobbDouglasAlpha,
|
|
);
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
it('should revert if `rewardDelegatedStakeWeight` is greater than PPM_DENOMINATOR', async () => {
|
|
const invalidStorageParams = {
|
|
...validStorageParams,
|
|
rewardDelegatedStakeWeight: new BigNumber(constants.PPM_DENOMINATOR + 1),
|
|
};
|
|
await testProxyContract.setTestStorageParams(invalidStorageParams).awaitTransactionSuccessAsync();
|
|
const tx = testProxyContract.assertValidStorageParams().callAsync();
|
|
const expectedError = new StakingRevertErrors.InvalidParamValueError(
|
|
StakingRevertErrors.InvalidParamValueErrorCodes.InvalidRewardDelegatedStakeWeight,
|
|
);
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
it('should revert if `minimumPoolStake` is less than two', async () => {
|
|
const invalidStorageParams = {
|
|
...validStorageParams,
|
|
minimumPoolStake: new BigNumber(1),
|
|
};
|
|
await testProxyContract.setTestStorageParams(invalidStorageParams).awaitTransactionSuccessAsync();
|
|
const tx = testProxyContract.assertValidStorageParams().callAsync();
|
|
const expectedError = new StakingRevertErrors.InvalidParamValueError(
|
|
StakingRevertErrors.InvalidParamValueErrorCodes.InvalidMinimumPoolStake,
|
|
);
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
});
|
|
});
|
|
// tslint:disable: max-file-line-count
|