* update abi-gen with new method interfaces * wip: get all packages to build * wip: get all packages to build * Fix two contract wrapper calls * Export necessary types part of the contract wrapper public interfaces * Revive and fix wrapper_unit_tests * Remove duplicate type * Fix lib_exchange_rich_error_decoder tests * Fix remaining test failures in contracts-* packages * Prettier fixes * remove transactionHelper * lint and update changelogs * Fix prettier * Revert changes to reference docs * Add back changelog already published and add revert changelog entry * Add missing CHANGELOG entries * Add missing comma * Update mesh-rpc-client dep * Update Mesh RPC logic in @0x/orderbook to v6.0.1-beta * Align package versions
368 lines
17 KiB
TypeScript
368 lines
17 KiB
TypeScript
import {
|
|
blockchainTests,
|
|
constants,
|
|
expect,
|
|
filterLogsToArguments,
|
|
hexLeftPad,
|
|
hexRandom,
|
|
toHex,
|
|
verifyEventsFromLogs,
|
|
} from '@0x/contracts-test-utils';
|
|
import { StakingRevertErrors } from '@0x/order-utils';
|
|
import { BigNumber, SafeMathRevertErrors } from '@0x/utils';
|
|
import * as _ from 'lodash';
|
|
|
|
import { artifacts } from '../artifacts';
|
|
import {
|
|
TestMixinStakingPoolContract,
|
|
TestMixinStakingPoolEvents,
|
|
TestMixinStakingPoolStakingPoolCreatedEventArgs as StakingPoolCreated,
|
|
} from '../wrappers';
|
|
|
|
blockchainTests.resets('MixinStakingPool unit tests', env => {
|
|
let testContract: TestMixinStakingPoolContract;
|
|
let operator: string;
|
|
let maker: string;
|
|
let notOperatorOrMaker: string;
|
|
|
|
before(async () => {
|
|
[operator, maker, notOperatorOrMaker] = await env.getAccountAddressesAsync();
|
|
testContract = await TestMixinStakingPoolContract.deployFrom0xArtifactAsync(
|
|
artifacts.TestMixinStakingPool,
|
|
env.provider,
|
|
env.txDefaults,
|
|
artifacts,
|
|
);
|
|
});
|
|
|
|
function toNextPoolId(lastPoolId: string): string {
|
|
return hexLeftPad(new BigNumber(lastPoolId.slice(2), 16).plus(1));
|
|
}
|
|
|
|
function randomOperatorShare(): number {
|
|
return _.random(0, constants.PPM_100_PERCENT);
|
|
}
|
|
|
|
interface CreatePoolOpts {
|
|
poolId: string;
|
|
operator: string;
|
|
operatorShare: number;
|
|
}
|
|
|
|
async function createPoolAsync(opts?: Partial<CreatePoolOpts>): Promise<CreatePoolOpts> {
|
|
const _opts = {
|
|
poolId: hexRandom(),
|
|
operator,
|
|
operatorShare: randomOperatorShare(),
|
|
...opts,
|
|
};
|
|
await testContract
|
|
.setPoolById(_opts.poolId, {
|
|
operator: _opts.operator,
|
|
operatorShare: _opts.operatorShare,
|
|
})
|
|
.awaitTransactionSuccessAsync();
|
|
return _opts;
|
|
}
|
|
|
|
async function addMakerToPoolAsync(poolId: string, _maker: string): Promise<void> {
|
|
await testContract.setPoolIdByMaker(poolId, _maker).awaitTransactionSuccessAsync();
|
|
}
|
|
|
|
describe('onlyStakingPoolOperator modifier', () => {
|
|
it('fails if not called by the pool operator', async () => {
|
|
const { poolId } = await createPoolAsync();
|
|
const tx = testContract.testOnlyStakingPoolOperatorModifier(poolId).callAsync({ from: notOperatorOrMaker });
|
|
const expectedError = new StakingRevertErrors.OnlyCallableByPoolOperatorError(notOperatorOrMaker, poolId);
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
it('fails if called by a pool maker', async () => {
|
|
const { poolId } = await createPoolAsync();
|
|
await addMakerToPoolAsync(poolId, maker);
|
|
const tx = testContract.testOnlyStakingPoolOperatorModifier(poolId).callAsync({ from: maker });
|
|
const expectedError = new StakingRevertErrors.OnlyCallableByPoolOperatorError(maker, poolId);
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
it('succeeds if called by the pool operator', async () => {
|
|
const { poolId } = await createPoolAsync();
|
|
await testContract.testOnlyStakingPoolOperatorModifier(poolId).callAsync({ from: operator });
|
|
});
|
|
});
|
|
|
|
describe('createStakingPool()', () => {
|
|
let nextPoolId: string;
|
|
|
|
before(async () => {
|
|
nextPoolId = toNextPoolId(await testContract.lastPoolId().callAsync());
|
|
});
|
|
|
|
it('fails if the next pool ID overflows', async () => {
|
|
await testContract.setLastPoolId(toHex(constants.MAX_UINT256)).awaitTransactionSuccessAsync();
|
|
const tx = testContract.createStakingPool(randomOperatorShare(), false).awaitTransactionSuccessAsync();
|
|
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
|
|
SafeMathRevertErrors.BinOpErrorCodes.AdditionOverflow,
|
|
constants.MAX_UINT256,
|
|
new BigNumber(1),
|
|
);
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
it('fails if the operator share is invalid', async () => {
|
|
const operatorShare = constants.PPM_100_PERCENT + 1;
|
|
const tx = testContract.createStakingPool(operatorShare, false).awaitTransactionSuccessAsync();
|
|
const expectedError = new StakingRevertErrors.OperatorShareError(
|
|
StakingRevertErrors.OperatorShareErrorCodes.OperatorShareTooLarge,
|
|
nextPoolId,
|
|
operatorShare,
|
|
);
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
it('operator can create and own multiple pools', async () => {
|
|
const { logs: logs1 } = await testContract
|
|
.createStakingPool(randomOperatorShare(), false)
|
|
.awaitTransactionSuccessAsync();
|
|
const { logs: logs2 } = await testContract
|
|
.createStakingPool(randomOperatorShare(), false)
|
|
.awaitTransactionSuccessAsync();
|
|
const createEvents = filterLogsToArguments<StakingPoolCreated>(
|
|
[...logs1, ...logs2],
|
|
TestMixinStakingPoolEvents.StakingPoolCreated,
|
|
);
|
|
expect(createEvents).to.be.length(2);
|
|
const poolIds = createEvents.map(e => e.poolId);
|
|
expect(poolIds[0]).to.not.eq(poolIds[1]);
|
|
const pools = await Promise.all(
|
|
poolIds.map(async poolId => testContract.getStakingPool(poolId).callAsync()),
|
|
);
|
|
expect(pools[0].operator).to.eq(pools[1].operator);
|
|
});
|
|
it('operator can only be maker of one pool', async () => {
|
|
await testContract.createStakingPool(randomOperatorShare(), true).awaitTransactionSuccessAsync();
|
|
const { logs } = await testContract
|
|
.createStakingPool(randomOperatorShare(), true)
|
|
.awaitTransactionSuccessAsync();
|
|
const createEvents = filterLogsToArguments<StakingPoolCreated>(
|
|
logs,
|
|
TestMixinStakingPoolEvents.StakingPoolCreated,
|
|
);
|
|
const makerPool = await testContract.poolIdByMaker(operator).callAsync();
|
|
expect(makerPool).to.eq(createEvents[0].poolId);
|
|
});
|
|
it('computes correct next pool ID', async () => {
|
|
const { logs } = await testContract
|
|
.createStakingPool(randomOperatorShare(), false)
|
|
.awaitTransactionSuccessAsync();
|
|
const createEvents = filterLogsToArguments<StakingPoolCreated>(
|
|
logs,
|
|
TestMixinStakingPoolEvents.StakingPoolCreated,
|
|
);
|
|
const poolId = createEvents[0].poolId;
|
|
expect(poolId).to.eq(nextPoolId);
|
|
});
|
|
it('increments last pool ID counter', async () => {
|
|
await testContract.createStakingPool(randomOperatorShare(), false).awaitTransactionSuccessAsync();
|
|
const lastPoolIdAfter = await testContract.lastPoolId().callAsync();
|
|
expect(lastPoolIdAfter).to.eq(nextPoolId);
|
|
});
|
|
it('records pool details', async () => {
|
|
const operatorShare = randomOperatorShare();
|
|
await testContract.createStakingPool(operatorShare, false).awaitTransactionSuccessAsync({ from: operator });
|
|
const pool = await testContract.getStakingPool(nextPoolId).callAsync();
|
|
expect(pool.operator).to.eq(operator);
|
|
expect(pool.operatorShare).to.bignumber.eq(operatorShare);
|
|
});
|
|
it('records pool details when operator share is 100%', async () => {
|
|
const operatorShare = constants.PPM_100_PERCENT;
|
|
await testContract.createStakingPool(operatorShare, false).awaitTransactionSuccessAsync({ from: operator });
|
|
const pool = await testContract.getStakingPool(nextPoolId).callAsync();
|
|
expect(pool.operator).to.eq(operator);
|
|
expect(pool.operatorShare).to.bignumber.eq(operatorShare);
|
|
});
|
|
it('records pool details when operator share is 0%', async () => {
|
|
const operatorShare = constants.ZERO_AMOUNT;
|
|
await testContract.createStakingPool(operatorShare, false).awaitTransactionSuccessAsync({ from: operator });
|
|
const pool = await testContract.getStakingPool(nextPoolId).callAsync();
|
|
expect(pool.operator).to.eq(operator);
|
|
expect(pool.operatorShare).to.bignumber.eq(operatorShare);
|
|
});
|
|
it('returns the next pool ID', async () => {
|
|
const poolId = await testContract.createStakingPool(randomOperatorShare(), false).callAsync({
|
|
from: operator,
|
|
});
|
|
expect(poolId).to.eq(nextPoolId);
|
|
});
|
|
it('can add operator as a maker', async () => {
|
|
const operatorShare = randomOperatorShare();
|
|
await testContract.createStakingPool(operatorShare, true).awaitTransactionSuccessAsync({ from: operator });
|
|
const makerPoolId = await testContract.poolIdByMaker(operator).callAsync();
|
|
expect(makerPoolId).to.eq(nextPoolId);
|
|
});
|
|
it('emits a `StakingPoolCreated` event', async () => {
|
|
const operatorShare = randomOperatorShare();
|
|
const { logs } = await testContract.createStakingPool(operatorShare, false).awaitTransactionSuccessAsync({
|
|
from: operator,
|
|
});
|
|
verifyEventsFromLogs(
|
|
logs,
|
|
[
|
|
{
|
|
poolId: nextPoolId,
|
|
operator,
|
|
operatorShare,
|
|
},
|
|
],
|
|
TestMixinStakingPoolEvents.StakingPoolCreated,
|
|
);
|
|
});
|
|
it('emits a `MakerStakingPoolSet` event when also joining as a maker', async () => {
|
|
const operatorShare = randomOperatorShare();
|
|
const { logs } = await testContract.createStakingPool(operatorShare, true).awaitTransactionSuccessAsync({
|
|
from: operator,
|
|
});
|
|
verifyEventsFromLogs(
|
|
logs,
|
|
[
|
|
{
|
|
makerAddress: operator,
|
|
poolId: nextPoolId,
|
|
},
|
|
],
|
|
TestMixinStakingPoolEvents.MakerStakingPoolSet,
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('decreaseStakingPoolOperatorShare()', () => {
|
|
it('fails if not called by operator', async () => {
|
|
const { poolId, operatorShare } = await createPoolAsync();
|
|
const tx = testContract
|
|
.decreaseStakingPoolOperatorShare(poolId, operatorShare - 1)
|
|
.awaitTransactionSuccessAsync({ from: notOperatorOrMaker });
|
|
const expectedError = new StakingRevertErrors.OnlyCallableByPoolOperatorError(notOperatorOrMaker, poolId);
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
it('fails if called by maker', async () => {
|
|
const { poolId, operatorShare } = await createPoolAsync();
|
|
await addMakerToPoolAsync(poolId, maker);
|
|
const tx = testContract
|
|
.decreaseStakingPoolOperatorShare(poolId, operatorShare - 1)
|
|
.awaitTransactionSuccessAsync({ from: maker });
|
|
const expectedError = new StakingRevertErrors.OnlyCallableByPoolOperatorError(maker, poolId);
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
it('fails if operator share is greater than current', async () => {
|
|
const { poolId, operatorShare } = await createPoolAsync();
|
|
const tx = testContract
|
|
.decreaseStakingPoolOperatorShare(poolId, operatorShare + 1)
|
|
.awaitTransactionSuccessAsync({ from: operator });
|
|
const expectedError = new StakingRevertErrors.OperatorShareError(
|
|
StakingRevertErrors.OperatorShareErrorCodes.CanOnlyDecreaseOperatorShare,
|
|
poolId,
|
|
operatorShare + 1,
|
|
);
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
it('fails if operator share is greater than PPM_100_PERCENT', async () => {
|
|
const { poolId } = await createPoolAsync();
|
|
const tx = testContract
|
|
.decreaseStakingPoolOperatorShare(poolId, constants.PPM_100_PERCENT + 1)
|
|
.awaitTransactionSuccessAsync({ from: operator });
|
|
const expectedError = new StakingRevertErrors.OperatorShareError(
|
|
StakingRevertErrors.OperatorShareErrorCodes.OperatorShareTooLarge,
|
|
poolId,
|
|
constants.PPM_100_PERCENT + 1,
|
|
);
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
it('records new operator share', async () => {
|
|
const { poolId, operatorShare } = await createPoolAsync();
|
|
await testContract
|
|
.decreaseStakingPoolOperatorShare(poolId, operatorShare - 1)
|
|
.awaitTransactionSuccessAsync({ from: operator });
|
|
const pool = await testContract.getStakingPool(poolId).callAsync();
|
|
expect(pool.operatorShare).to.bignumber.eq(operatorShare - 1);
|
|
});
|
|
it('does not modify operator share if equal to current', async () => {
|
|
const { poolId, operatorShare } = await createPoolAsync();
|
|
await testContract.decreaseStakingPoolOperatorShare(poolId, operatorShare).awaitTransactionSuccessAsync({
|
|
from: operator,
|
|
});
|
|
const pool = await testContract.getStakingPool(poolId).callAsync();
|
|
expect(pool.operatorShare).to.bignumber.eq(operatorShare);
|
|
});
|
|
it('does not modify operator', async () => {
|
|
const { poolId, operatorShare } = await createPoolAsync();
|
|
await testContract
|
|
.decreaseStakingPoolOperatorShare(poolId, operatorShare - 1)
|
|
.awaitTransactionSuccessAsync({ from: operator });
|
|
const pool = await testContract.getStakingPool(poolId).callAsync();
|
|
expect(pool.operator).to.eq(operator);
|
|
});
|
|
it('emits an `OperatorShareDecreased` event', async () => {
|
|
const { poolId, operatorShare } = await createPoolAsync();
|
|
const { logs } = await testContract
|
|
.decreaseStakingPoolOperatorShare(poolId, operatorShare - 1)
|
|
.awaitTransactionSuccessAsync({ from: operator });
|
|
verifyEventsFromLogs(
|
|
logs,
|
|
[
|
|
{
|
|
poolId,
|
|
oldOperatorShare: operatorShare,
|
|
newOperatorShare: operatorShare - 1,
|
|
},
|
|
],
|
|
TestMixinStakingPoolEvents.OperatorShareDecreased,
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('joinStakingPoolAsMaker()', () => {
|
|
it('records sender as maker for the pool', async () => {
|
|
const { poolId } = await createPoolAsync();
|
|
await testContract.joinStakingPoolAsMaker(poolId).awaitTransactionSuccessAsync({ from: maker });
|
|
const makerPoolId = await testContract.poolIdByMaker(maker).callAsync();
|
|
expect(makerPoolId).to.eq(poolId);
|
|
});
|
|
it('operator can join as maker for the pool', async () => {
|
|
const { poolId } = await createPoolAsync();
|
|
await testContract.joinStakingPoolAsMaker(poolId).awaitTransactionSuccessAsync({ from: operator });
|
|
const makerPoolId = await testContract.poolIdByMaker(operator).callAsync();
|
|
expect(makerPoolId).to.eq(poolId);
|
|
});
|
|
it('can join the same pool as a maker twice', async () => {
|
|
const { poolId } = await createPoolAsync();
|
|
await testContract.joinStakingPoolAsMaker(poolId).awaitTransactionSuccessAsync({ from: maker });
|
|
await testContract.joinStakingPoolAsMaker(poolId).awaitTransactionSuccessAsync({ from: maker });
|
|
const makerPoolId = await testContract.poolIdByMaker(maker).callAsync();
|
|
expect(makerPoolId).to.eq(poolId);
|
|
});
|
|
it('can only be a maker in one pool at a time', async () => {
|
|
const { poolId: poolId1 } = await createPoolAsync();
|
|
const { poolId: poolId2 } = await createPoolAsync();
|
|
await testContract.joinStakingPoolAsMaker(poolId1).awaitTransactionSuccessAsync({ from: maker });
|
|
let makerPoolId = await testContract.poolIdByMaker(maker).callAsync();
|
|
expect(makerPoolId).to.eq(poolId1);
|
|
await testContract.joinStakingPoolAsMaker(poolId2).awaitTransactionSuccessAsync({ from: maker });
|
|
makerPoolId = await testContract.poolIdByMaker(maker).callAsync();
|
|
expect(makerPoolId).to.eq(poolId2);
|
|
});
|
|
it('emits a `MakerStakingPoolSet` event', async () => {
|
|
const { poolId } = await createPoolAsync();
|
|
const { logs } = await testContract.joinStakingPoolAsMaker(poolId).awaitTransactionSuccessAsync({
|
|
from: maker,
|
|
});
|
|
verifyEventsFromLogs(
|
|
logs,
|
|
[
|
|
{
|
|
makerAddress: maker,
|
|
poolId,
|
|
},
|
|
],
|
|
TestMixinStakingPoolEvents.MakerStakingPoolSet,
|
|
);
|
|
});
|
|
});
|
|
});
|
|
// tslint:disable: max-file-line-count
|