475 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			475 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
import {
 | 
						|
    blockchainTests,
 | 
						|
    constants,
 | 
						|
    expect,
 | 
						|
    filterLogsToArguments,
 | 
						|
    getRandomInteger,
 | 
						|
    Numberish,
 | 
						|
    randomAddress,
 | 
						|
} from '@0x/contracts-test-utils';
 | 
						|
import { BigNumber, hexUtils, StakingRevertErrors } from '@0x/utils';
 | 
						|
import { LogEntry } from 'ethereum-types';
 | 
						|
import * as _ from 'lodash';
 | 
						|
 | 
						|
import { artifacts } from '../artifacts';
 | 
						|
import {
 | 
						|
    IStakingEventsEvents,
 | 
						|
    IStakingEventsStakingPoolEarnedRewardsInEpochEventArgs,
 | 
						|
    TestProtocolFeesContract,
 | 
						|
    TestProtocolFeesERC20ProxyTransferFromEventArgs,
 | 
						|
    TestProtocolFeesEvents,
 | 
						|
} from '../wrappers';
 | 
						|
 | 
						|
blockchainTests('Protocol Fees unit tests', env => {
 | 
						|
    let ownerAddress: string;
 | 
						|
    let exchangeAddress: string;
 | 
						|
    let notExchangeAddress: string;
 | 
						|
    let testContract: TestProtocolFeesContract;
 | 
						|
    let minimumStake: BigNumber;
 | 
						|
 | 
						|
    before(async () => {
 | 
						|
        [ownerAddress, exchangeAddress, notExchangeAddress] = await env.web3Wrapper.getAvailableAddressesAsync();
 | 
						|
 | 
						|
        // Deploy the protocol fees contract.
 | 
						|
        testContract = await TestProtocolFeesContract.deployFrom0xArtifactAsync(
 | 
						|
            artifacts.TestProtocolFees,
 | 
						|
            env.provider,
 | 
						|
            {
 | 
						|
                ...env.txDefaults,
 | 
						|
                from: ownerAddress,
 | 
						|
            },
 | 
						|
            artifacts,
 | 
						|
            exchangeAddress,
 | 
						|
        );
 | 
						|
 | 
						|
        minimumStake = (await testContract.getParams().callAsync())[2];
 | 
						|
    });
 | 
						|
 | 
						|
    interface CreateTestPoolOpts {
 | 
						|
        poolId: string;
 | 
						|
        operatorStake: Numberish;
 | 
						|
        membersStake: Numberish;
 | 
						|
        makers: string[];
 | 
						|
    }
 | 
						|
 | 
						|
    async function createTestPoolAsync(opts?: Partial<CreateTestPoolOpts>): Promise<CreateTestPoolOpts> {
 | 
						|
        const _opts = {
 | 
						|
            poolId: hexUtils.random(),
 | 
						|
            operatorStake: getRandomInteger(minimumStake, '100e18'),
 | 
						|
            membersStake: getRandomInteger(minimumStake, '100e18'),
 | 
						|
            makers: _.times(2, () => randomAddress()),
 | 
						|
            ...opts,
 | 
						|
        };
 | 
						|
        await testContract
 | 
						|
            .createTestPool(
 | 
						|
                _opts.poolId,
 | 
						|
                new BigNumber(_opts.operatorStake),
 | 
						|
                new BigNumber(_opts.membersStake),
 | 
						|
                _opts.makers,
 | 
						|
            )
 | 
						|
            .awaitTransactionSuccessAsync();
 | 
						|
        return _opts;
 | 
						|
    }
 | 
						|
 | 
						|
    blockchainTests.resets('payProtocolFee()', () => {
 | 
						|
        const DEFAULT_PROTOCOL_FEE_PAID = new BigNumber(150e3).times(1e9);
 | 
						|
        const { ZERO_AMOUNT } = constants;
 | 
						|
        const makerAddress = randomAddress();
 | 
						|
        const payerAddress = randomAddress();
 | 
						|
 | 
						|
        describe('forbidden actions', () => {
 | 
						|
            it('should revert if called by a non-exchange', async () => {
 | 
						|
                const tx = testContract
 | 
						|
                    .payProtocolFee(makerAddress, payerAddress, DEFAULT_PROTOCOL_FEE_PAID)
 | 
						|
                    .awaitTransactionSuccessAsync({ from: notExchangeAddress });
 | 
						|
                const expectedError = new StakingRevertErrors.OnlyCallableByExchangeError(notExchangeAddress);
 | 
						|
                return expect(tx).to.revertWith(expectedError);
 | 
						|
            });
 | 
						|
 | 
						|
            it('should revert if `protocolFee` is zero with non-zero value sent', async () => {
 | 
						|
                const tx = testContract
 | 
						|
                    .payProtocolFee(makerAddress, payerAddress, ZERO_AMOUNT)
 | 
						|
                    .awaitTransactionSuccessAsync({ from: exchangeAddress, value: DEFAULT_PROTOCOL_FEE_PAID });
 | 
						|
                const expectedError = new StakingRevertErrors.InvalidProtocolFeePaymentError(
 | 
						|
                    ZERO_AMOUNT,
 | 
						|
                    DEFAULT_PROTOCOL_FEE_PAID,
 | 
						|
                );
 | 
						|
                return expect(tx).to.revertWith(expectedError);
 | 
						|
            });
 | 
						|
 | 
						|
            it('should revert if `protocolFee` is < than the provided message value', async () => {
 | 
						|
                const tx = testContract
 | 
						|
                    .payProtocolFee(makerAddress, payerAddress, DEFAULT_PROTOCOL_FEE_PAID)
 | 
						|
                    .awaitTransactionSuccessAsync({ from: exchangeAddress, value: DEFAULT_PROTOCOL_FEE_PAID.minus(1) });
 | 
						|
                const expectedError = new StakingRevertErrors.InvalidProtocolFeePaymentError(
 | 
						|
                    DEFAULT_PROTOCOL_FEE_PAID,
 | 
						|
                    DEFAULT_PROTOCOL_FEE_PAID.minus(1),
 | 
						|
                );
 | 
						|
                return expect(tx).to.revertWith(expectedError);
 | 
						|
            });
 | 
						|
 | 
						|
            it('should revert if `protocolFee` is > than the provided message value', async () => {
 | 
						|
                const tx = testContract
 | 
						|
                    .payProtocolFee(makerAddress, payerAddress, DEFAULT_PROTOCOL_FEE_PAID)
 | 
						|
                    .awaitTransactionSuccessAsync({ from: exchangeAddress, value: DEFAULT_PROTOCOL_FEE_PAID.plus(1) });
 | 
						|
                const expectedError = new StakingRevertErrors.InvalidProtocolFeePaymentError(
 | 
						|
                    DEFAULT_PROTOCOL_FEE_PAID,
 | 
						|
                    DEFAULT_PROTOCOL_FEE_PAID.plus(1),
 | 
						|
                );
 | 
						|
                return expect(tx).to.revertWith(expectedError);
 | 
						|
            });
 | 
						|
        });
 | 
						|
 | 
						|
        async function getProtocolFeesAsync(poolId: string): Promise<BigNumber> {
 | 
						|
            return (await testContract.getStakingPoolStatsThisEpoch(poolId).callAsync()).feesCollected;
 | 
						|
        }
 | 
						|
 | 
						|
        describe('ETH fees', () => {
 | 
						|
            function assertNoWETHTransferLogs(logs: LogEntry[]): void {
 | 
						|
                const logsArgs = filterLogsToArguments<TestProtocolFeesERC20ProxyTransferFromEventArgs>(
 | 
						|
                    logs,
 | 
						|
                    TestProtocolFeesEvents.ERC20ProxyTransferFrom,
 | 
						|
                );
 | 
						|
                expect(logsArgs).to.deep.eq([]);
 | 
						|
            }
 | 
						|
 | 
						|
            it('should not transfer WETH if value is sent', async () => {
 | 
						|
                await createTestPoolAsync({ operatorStake: minimumStake });
 | 
						|
                const receipt = await testContract
 | 
						|
                    .payProtocolFee(makerAddress, payerAddress, DEFAULT_PROTOCOL_FEE_PAID)
 | 
						|
                    .awaitTransactionSuccessAsync({ from: exchangeAddress, value: DEFAULT_PROTOCOL_FEE_PAID });
 | 
						|
                assertNoWETHTransferLogs(receipt.logs);
 | 
						|
            });
 | 
						|
 | 
						|
            it('should credit pool if the maker is in a pool', async () => {
 | 
						|
                const { poolId } = await createTestPoolAsync({ operatorStake: minimumStake, makers: [makerAddress] });
 | 
						|
                const receipt = await testContract
 | 
						|
                    .payProtocolFee(makerAddress, payerAddress, DEFAULT_PROTOCOL_FEE_PAID)
 | 
						|
                    .awaitTransactionSuccessAsync({ from: exchangeAddress, value: DEFAULT_PROTOCOL_FEE_PAID });
 | 
						|
 | 
						|
                assertNoWETHTransferLogs(receipt.logs);
 | 
						|
                const poolFees = await getProtocolFeesAsync(poolId);
 | 
						|
                expect(poolFees).to.bignumber.eq(DEFAULT_PROTOCOL_FEE_PAID);
 | 
						|
            });
 | 
						|
 | 
						|
            it('should not credit the pool if maker is not in a pool', async () => {
 | 
						|
                const { poolId } = await createTestPoolAsync({ operatorStake: minimumStake });
 | 
						|
                const receipt = await testContract
 | 
						|
                    .payProtocolFee(makerAddress, payerAddress, DEFAULT_PROTOCOL_FEE_PAID)
 | 
						|
                    .awaitTransactionSuccessAsync({ from: exchangeAddress, value: DEFAULT_PROTOCOL_FEE_PAID });
 | 
						|
                assertNoWETHTransferLogs(receipt.logs);
 | 
						|
                const poolFees = await getProtocolFeesAsync(poolId);
 | 
						|
                expect(poolFees).to.bignumber.eq(ZERO_AMOUNT);
 | 
						|
            });
 | 
						|
 | 
						|
            it('fees paid to the same maker should go to the same pool', async () => {
 | 
						|
                const { poolId } = await createTestPoolAsync({ operatorStake: minimumStake, makers: [makerAddress] });
 | 
						|
                const payAsync = async () => {
 | 
						|
                    const receipt = await testContract
 | 
						|
                        .payProtocolFee(makerAddress, payerAddress, DEFAULT_PROTOCOL_FEE_PAID)
 | 
						|
                        .awaitTransactionSuccessAsync({ from: exchangeAddress, value: DEFAULT_PROTOCOL_FEE_PAID });
 | 
						|
                    assertNoWETHTransferLogs(receipt.logs);
 | 
						|
                };
 | 
						|
                await payAsync();
 | 
						|
                await payAsync();
 | 
						|
                const expectedTotalFees = DEFAULT_PROTOCOL_FEE_PAID.times(2);
 | 
						|
                const poolFees = await getProtocolFeesAsync(poolId);
 | 
						|
                expect(poolFees).to.bignumber.eq(expectedTotalFees);
 | 
						|
            });
 | 
						|
        });
 | 
						|
 | 
						|
        describe('WETH fees', () => {
 | 
						|
            function assertWETHTransferLogs(logs: LogEntry[], fromAddress: string, amount: BigNumber): void {
 | 
						|
                const logsArgs = filterLogsToArguments<TestProtocolFeesERC20ProxyTransferFromEventArgs>(
 | 
						|
                    logs,
 | 
						|
                    TestProtocolFeesEvents.ERC20ProxyTransferFrom,
 | 
						|
                );
 | 
						|
                expect(logsArgs.length).to.eq(1);
 | 
						|
                for (const args of logsArgs) {
 | 
						|
                    expect(args.from).to.eq(fromAddress);
 | 
						|
                    expect(args.to).to.eq(testContract.address);
 | 
						|
                    expect(args.amount).to.bignumber.eq(amount);
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            it('should transfer WETH if no value is sent and the maker is not in a pool', async () => {
 | 
						|
                await createTestPoolAsync({ operatorStake: minimumStake });
 | 
						|
                const receipt = await testContract
 | 
						|
                    .payProtocolFee(makerAddress, payerAddress, DEFAULT_PROTOCOL_FEE_PAID)
 | 
						|
                    .awaitTransactionSuccessAsync({ from: exchangeAddress, value: ZERO_AMOUNT });
 | 
						|
                assertWETHTransferLogs(receipt.logs, payerAddress, DEFAULT_PROTOCOL_FEE_PAID);
 | 
						|
            });
 | 
						|
 | 
						|
            it('should update `protocolFeesThisEpochByPool` if the maker is in a pool', async () => {
 | 
						|
                const { poolId } = await createTestPoolAsync({ operatorStake: minimumStake, makers: [makerAddress] });
 | 
						|
                const receipt = await testContract
 | 
						|
                    .payProtocolFee(makerAddress, payerAddress, DEFAULT_PROTOCOL_FEE_PAID)
 | 
						|
                    .awaitTransactionSuccessAsync({ from: exchangeAddress, value: ZERO_AMOUNT });
 | 
						|
                assertWETHTransferLogs(receipt.logs, payerAddress, DEFAULT_PROTOCOL_FEE_PAID);
 | 
						|
                const poolFees = await getProtocolFeesAsync(poolId);
 | 
						|
                expect(poolFees).to.bignumber.eq(DEFAULT_PROTOCOL_FEE_PAID);
 | 
						|
            });
 | 
						|
 | 
						|
            it('should not update `protocolFeesThisEpochByPool` if maker is not in a pool', async () => {
 | 
						|
                const { poolId } = await createTestPoolAsync({ operatorStake: minimumStake });
 | 
						|
                const receipt = await testContract
 | 
						|
                    .payProtocolFee(makerAddress, payerAddress, DEFAULT_PROTOCOL_FEE_PAID)
 | 
						|
                    .awaitTransactionSuccessAsync({ from: exchangeAddress, value: ZERO_AMOUNT });
 | 
						|
                assertWETHTransferLogs(receipt.logs, payerAddress, DEFAULT_PROTOCOL_FEE_PAID);
 | 
						|
                const poolFees = await getProtocolFeesAsync(poolId);
 | 
						|
                expect(poolFees).to.bignumber.eq(ZERO_AMOUNT);
 | 
						|
            });
 | 
						|
 | 
						|
            it('fees paid to the same maker should go to the same pool', async () => {
 | 
						|
                const { poolId } = await createTestPoolAsync({ operatorStake: minimumStake, makers: [makerAddress] });
 | 
						|
                const payAsync = async () => {
 | 
						|
                    const receipt = await testContract
 | 
						|
                        .payProtocolFee(makerAddress, payerAddress, DEFAULT_PROTOCOL_FEE_PAID)
 | 
						|
                        .awaitTransactionSuccessAsync({ from: exchangeAddress, value: ZERO_AMOUNT });
 | 
						|
                    assertWETHTransferLogs(receipt.logs, payerAddress, DEFAULT_PROTOCOL_FEE_PAID);
 | 
						|
                };
 | 
						|
                await payAsync();
 | 
						|
                await payAsync();
 | 
						|
                const expectedTotalFees = DEFAULT_PROTOCOL_FEE_PAID.times(2);
 | 
						|
                const poolFees = await getProtocolFeesAsync(poolId);
 | 
						|
                expect(poolFees).to.bignumber.eq(expectedTotalFees);
 | 
						|
            });
 | 
						|
 | 
						|
            it('fees paid to the same maker in WETH then ETH should go to the same pool', async () => {
 | 
						|
                const { poolId } = await createTestPoolAsync({ operatorStake: minimumStake, makers: [makerAddress] });
 | 
						|
                const payAsync = async (inWETH: boolean) => {
 | 
						|
                    await testContract
 | 
						|
                        .payProtocolFee(makerAddress, payerAddress, DEFAULT_PROTOCOL_FEE_PAID)
 | 
						|
                        .awaitTransactionSuccessAsync({
 | 
						|
                            from: exchangeAddress,
 | 
						|
                            value: inWETH ? ZERO_AMOUNT : DEFAULT_PROTOCOL_FEE_PAID,
 | 
						|
                        });
 | 
						|
                };
 | 
						|
                await payAsync(true);
 | 
						|
                await payAsync(false);
 | 
						|
                const expectedTotalFees = DEFAULT_PROTOCOL_FEE_PAID.times(2);
 | 
						|
                const poolFees = await getProtocolFeesAsync(poolId);
 | 
						|
                expect(poolFees).to.bignumber.eq(expectedTotalFees);
 | 
						|
            });
 | 
						|
        });
 | 
						|
 | 
						|
        describe('Dust stake', () => {
 | 
						|
            it('credits pools with stake > minimum', async () => {
 | 
						|
                const { poolId } = await createTestPoolAsync({
 | 
						|
                    operatorStake: minimumStake.plus(1),
 | 
						|
                    membersStake: 0,
 | 
						|
                    makers: [makerAddress],
 | 
						|
                });
 | 
						|
                await testContract
 | 
						|
                    .payProtocolFee(makerAddress, constants.NULL_ADDRESS, DEFAULT_PROTOCOL_FEE_PAID)
 | 
						|
                    .awaitTransactionSuccessAsync({ from: exchangeAddress, value: DEFAULT_PROTOCOL_FEE_PAID });
 | 
						|
                const feesCredited = await getProtocolFeesAsync(poolId);
 | 
						|
                expect(feesCredited).to.bignumber.eq(DEFAULT_PROTOCOL_FEE_PAID);
 | 
						|
            });
 | 
						|
 | 
						|
            it('credits pools with stake == minimum', async () => {
 | 
						|
                const { poolId } = await createTestPoolAsync({
 | 
						|
                    operatorStake: minimumStake,
 | 
						|
                    membersStake: 0,
 | 
						|
                    makers: [makerAddress],
 | 
						|
                });
 | 
						|
                await testContract
 | 
						|
                    .payProtocolFee(makerAddress, constants.NULL_ADDRESS, DEFAULT_PROTOCOL_FEE_PAID)
 | 
						|
                    .awaitTransactionSuccessAsync({ from: exchangeAddress, value: DEFAULT_PROTOCOL_FEE_PAID });
 | 
						|
                const feesCredited = await getProtocolFeesAsync(poolId);
 | 
						|
                expect(feesCredited).to.bignumber.eq(DEFAULT_PROTOCOL_FEE_PAID);
 | 
						|
            });
 | 
						|
 | 
						|
            it('does not credit pools with stake < minimum', async () => {
 | 
						|
                const { poolId } = await createTestPoolAsync({
 | 
						|
                    operatorStake: minimumStake.minus(1),
 | 
						|
                    membersStake: 0,
 | 
						|
                    makers: [makerAddress],
 | 
						|
                });
 | 
						|
                await testContract
 | 
						|
                    .payProtocolFee(makerAddress, constants.NULL_ADDRESS, DEFAULT_PROTOCOL_FEE_PAID)
 | 
						|
                    .awaitTransactionSuccessAsync({ from: exchangeAddress, value: DEFAULT_PROTOCOL_FEE_PAID });
 | 
						|
                const feesCredited = await getProtocolFeesAsync(poolId);
 | 
						|
                expect(feesCredited).to.bignumber.eq(0);
 | 
						|
            });
 | 
						|
        });
 | 
						|
 | 
						|
        blockchainTests.resets('Finalization', () => {
 | 
						|
            let membersStakeWeight: number;
 | 
						|
 | 
						|
            before(async () => {
 | 
						|
                membersStakeWeight = (await testContract.getParams().callAsync())[1];
 | 
						|
            });
 | 
						|
 | 
						|
            interface FinalizationState {
 | 
						|
                numPoolsToFinalize: BigNumber;
 | 
						|
                totalFeesCollected: BigNumber;
 | 
						|
                totalWeightedStake: BigNumber;
 | 
						|
            }
 | 
						|
 | 
						|
            async function getFinalizationStateAsync(): Promise<FinalizationState> {
 | 
						|
                const aggregatedStats = await testContract.getAggregatedStatsForCurrentEpoch().callAsync();
 | 
						|
                return {
 | 
						|
                    numPoolsToFinalize: aggregatedStats.numPoolsToFinalize,
 | 
						|
                    totalFeesCollected: aggregatedStats.totalFeesCollected,
 | 
						|
                    totalWeightedStake: aggregatedStats.totalWeightedStake,
 | 
						|
                };
 | 
						|
            }
 | 
						|
 | 
						|
            interface PayToMakerResult {
 | 
						|
                poolEarnedRewardsEvents: IStakingEventsStakingPoolEarnedRewardsInEpochEventArgs[];
 | 
						|
                fee: BigNumber;
 | 
						|
            }
 | 
						|
 | 
						|
            async function payToMakerAsync(poolMaker: string, fee?: Numberish): Promise<PayToMakerResult> {
 | 
						|
                const _fee = fee === undefined ? getRandomInteger(1, '1e18') : fee;
 | 
						|
                const receipt = await testContract
 | 
						|
                    .payProtocolFee(poolMaker, payerAddress, new BigNumber(_fee))
 | 
						|
                    .awaitTransactionSuccessAsync({ from: exchangeAddress, value: _fee });
 | 
						|
                const events = filterLogsToArguments<IStakingEventsStakingPoolEarnedRewardsInEpochEventArgs>(
 | 
						|
                    receipt.logs,
 | 
						|
                    IStakingEventsEvents.StakingPoolEarnedRewardsInEpoch,
 | 
						|
                );
 | 
						|
                return {
 | 
						|
                    fee: new BigNumber(_fee),
 | 
						|
                    poolEarnedRewardsEvents: events,
 | 
						|
                };
 | 
						|
            }
 | 
						|
 | 
						|
            function toWeightedStake(operatorStake: Numberish, membersStake: Numberish): BigNumber {
 | 
						|
                return new BigNumber(membersStake)
 | 
						|
                    .times(membersStakeWeight)
 | 
						|
                    .dividedToIntegerBy(constants.PPM_DENOMINATOR)
 | 
						|
                    .plus(operatorStake);
 | 
						|
            }
 | 
						|
 | 
						|
            it('no pools to finalize to start', async () => {
 | 
						|
                const state = await getFinalizationStateAsync();
 | 
						|
                expect(state.numPoolsToFinalize).to.bignumber.eq(0);
 | 
						|
                expect(state.totalFeesCollected).to.bignumber.eq(0);
 | 
						|
                expect(state.totalWeightedStake).to.bignumber.eq(0);
 | 
						|
            });
 | 
						|
 | 
						|
            it('pool is not registered to start', async () => {
 | 
						|
                const { poolId } = await createTestPoolAsync();
 | 
						|
                const pool = await testContract.getStakingPoolStatsThisEpoch(poolId).callAsync();
 | 
						|
                expect(pool.feesCollected).to.bignumber.eq(0);
 | 
						|
                expect(pool.membersStake).to.bignumber.eq(0);
 | 
						|
                expect(pool.weightedStake).to.bignumber.eq(0);
 | 
						|
            });
 | 
						|
 | 
						|
            it('correctly emits event for pool the first time it earns a fee', async () => {
 | 
						|
                const pool = await createTestPoolAsync();
 | 
						|
                const {
 | 
						|
                    poolId,
 | 
						|
                    makers: [poolMaker],
 | 
						|
                } = pool;
 | 
						|
                const { fee, poolEarnedRewardsEvents } = await payToMakerAsync(poolMaker);
 | 
						|
                expect(poolEarnedRewardsEvents.length).to.eq(1);
 | 
						|
                expect(poolEarnedRewardsEvents[0].poolId).to.eq(poolId);
 | 
						|
                const actualPoolStats = await testContract.getStakingPoolStatsThisEpoch(poolId).callAsync();
 | 
						|
                const expectedWeightedStake = toWeightedStake(pool.operatorStake, pool.membersStake);
 | 
						|
                expect(actualPoolStats.feesCollected).to.bignumber.eq(fee);
 | 
						|
                expect(actualPoolStats.membersStake).to.bignumber.eq(pool.membersStake);
 | 
						|
                expect(actualPoolStats.weightedStake).to.bignumber.eq(expectedWeightedStake);
 | 
						|
                const state = await getFinalizationStateAsync();
 | 
						|
                expect(state.numPoolsToFinalize).to.bignumber.eq(1);
 | 
						|
                expect(state.totalFeesCollected).to.bignumber.eq(fee);
 | 
						|
                expect(state.totalWeightedStake).to.bignumber.eq(expectedWeightedStake);
 | 
						|
            });
 | 
						|
 | 
						|
            it('only adds to the already activated pool in the same epoch', async () => {
 | 
						|
                const pool = await createTestPoolAsync();
 | 
						|
                const {
 | 
						|
                    poolId,
 | 
						|
                    makers: [poolMaker],
 | 
						|
                } = pool;
 | 
						|
                const { fee: fee1 } = await payToMakerAsync(poolMaker);
 | 
						|
                const { fee: fee2, poolEarnedRewardsEvents } = await payToMakerAsync(poolMaker);
 | 
						|
                expect(poolEarnedRewardsEvents).to.deep.eq([]);
 | 
						|
                const actualPoolStats = await testContract.getStakingPoolStatsThisEpoch(poolId).callAsync();
 | 
						|
                const expectedWeightedStake = toWeightedStake(pool.operatorStake, pool.membersStake);
 | 
						|
                const fees = BigNumber.sum(fee1, fee2);
 | 
						|
                expect(actualPoolStats.feesCollected).to.bignumber.eq(fees);
 | 
						|
                expect(actualPoolStats.membersStake).to.bignumber.eq(pool.membersStake);
 | 
						|
                expect(actualPoolStats.weightedStake).to.bignumber.eq(expectedWeightedStake);
 | 
						|
                const state = await getFinalizationStateAsync();
 | 
						|
                expect(state.numPoolsToFinalize).to.bignumber.eq(1);
 | 
						|
                expect(state.totalFeesCollected).to.bignumber.eq(fees);
 | 
						|
                expect(state.totalWeightedStake).to.bignumber.eq(expectedWeightedStake);
 | 
						|
            });
 | 
						|
 | 
						|
            it('can activate multiple pools in the same epoch', async () => {
 | 
						|
                const pools = await Promise.all(_.times(3, async () => createTestPoolAsync()));
 | 
						|
                let totalFees = new BigNumber(0);
 | 
						|
                let totalWeightedStake = new BigNumber(0);
 | 
						|
                for (const pool of pools) {
 | 
						|
                    const {
 | 
						|
                        poolId,
 | 
						|
                        makers: [poolMaker],
 | 
						|
                    } = pool;
 | 
						|
                    const { fee, poolEarnedRewardsEvents } = await payToMakerAsync(poolMaker);
 | 
						|
                    expect(poolEarnedRewardsEvents.length).to.eq(1);
 | 
						|
                    expect(poolEarnedRewardsEvents[0].poolId).to.eq(poolId);
 | 
						|
                    const actualPoolStats = await testContract.getStakingPoolStatsThisEpoch(poolId).callAsync();
 | 
						|
                    const expectedWeightedStake = toWeightedStake(pool.operatorStake, pool.membersStake);
 | 
						|
                    expect(actualPoolStats.feesCollected).to.bignumber.eq(fee);
 | 
						|
                    expect(actualPoolStats.membersStake).to.bignumber.eq(pool.membersStake);
 | 
						|
                    expect(actualPoolStats.weightedStake).to.bignumber.eq(expectedWeightedStake);
 | 
						|
                    totalFees = totalFees.plus(fee);
 | 
						|
                    totalWeightedStake = totalWeightedStake.plus(expectedWeightedStake);
 | 
						|
                }
 | 
						|
                const state = await getFinalizationStateAsync();
 | 
						|
                expect(state.numPoolsToFinalize).to.bignumber.eq(pools.length);
 | 
						|
                expect(state.totalFeesCollected).to.bignumber.eq(totalFees);
 | 
						|
                expect(state.totalWeightedStake).to.bignumber.eq(totalWeightedStake);
 | 
						|
            });
 | 
						|
 | 
						|
            it('resets the pool after the epoch advances', async () => {
 | 
						|
                const pool = await createTestPoolAsync();
 | 
						|
                const {
 | 
						|
                    poolId,
 | 
						|
                    makers: [poolMaker],
 | 
						|
                } = pool;
 | 
						|
                await payToMakerAsync(poolMaker);
 | 
						|
                await testContract.advanceEpoch().awaitTransactionSuccessAsync();
 | 
						|
                const actualPoolStats = await testContract.getStakingPoolStatsThisEpoch(poolId).callAsync();
 | 
						|
                expect(actualPoolStats.feesCollected).to.bignumber.eq(0);
 | 
						|
                expect(actualPoolStats.membersStake).to.bignumber.eq(0);
 | 
						|
                expect(actualPoolStats.weightedStake).to.bignumber.eq(0);
 | 
						|
            });
 | 
						|
 | 
						|
            describe('Multiple makers', () => {
 | 
						|
                it('fees paid to different makers in the same pool go to that pool', async () => {
 | 
						|
                    const { poolId, makers } = await createTestPoolAsync();
 | 
						|
                    const { fee: fee1 } = await payToMakerAsync(makers[0]);
 | 
						|
                    const { fee: fee2 } = await payToMakerAsync(makers[1]);
 | 
						|
                    const expectedTotalFees = BigNumber.sum(fee1, fee2);
 | 
						|
                    const poolFees = await getProtocolFeesAsync(poolId);
 | 
						|
                    expect(poolFees).to.bignumber.eq(expectedTotalFees);
 | 
						|
                });
 | 
						|
 | 
						|
                it('fees paid to makers in different pools go to their respective pools', async () => {
 | 
						|
                    const {
 | 
						|
                        poolId: poolId1,
 | 
						|
                        makers: [maker1],
 | 
						|
                    } = await createTestPoolAsync();
 | 
						|
                    const {
 | 
						|
                        poolId: poolId2,
 | 
						|
                        makers: [maker2],
 | 
						|
                    } = await createTestPoolAsync();
 | 
						|
                    const { fee: fee1 } = await payToMakerAsync(maker1);
 | 
						|
                    const { fee: fee2 } = await payToMakerAsync(maker2);
 | 
						|
                    const [poolFees, otherPoolFees] = await Promise.all([
 | 
						|
                        getProtocolFeesAsync(poolId1),
 | 
						|
                        getProtocolFeesAsync(poolId2),
 | 
						|
                    ]);
 | 
						|
                    expect(poolFees).to.bignumber.eq(fee1);
 | 
						|
                    expect(otherPoolFees).to.bignumber.eq(fee2);
 | 
						|
                });
 | 
						|
            });
 | 
						|
        });
 | 
						|
    });
 | 
						|
});
 | 
						|
// tslint:disable: max-file-line-count
 |