* `@0x/contracts-zero-ex`: Govern `FeeCollector`s through a separate `FeeCollectorController` contract. * `@0x/contracts-integrations`: Fix broken EP protocol fees test. `@0x/contracts-integrations`: Make this package private. * `@0x/contract-addresses`: Update ganache snapshot addresses. * Update contracts/zero-ex/contracts/src/external/FeeCollectorController.sol Co-authored-by: mzhu25 <mchl.zhu.96@gmail.com> * Apply suggestions from code review Co-authored-by: mzhu25 <mchl.zhu.96@gmail.com> * rebase Co-authored-by: Lawrence Forman <me@merklejerk.com> Co-authored-by: mzhu25 <mchl.zhu.96@gmail.com>
		
			
				
	
	
		
			151 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			151 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
import { blockchainTests, expect } from '@0x/contracts-test-utils';
 | 
						|
import { AuthorizableRevertErrors, BigNumber, hexUtils } from '@0x/utils';
 | 
						|
 | 
						|
import { artifacts } from './artifacts';
 | 
						|
import {
 | 
						|
    FeeCollectorContract,
 | 
						|
    FeeCollectorControllerContract,
 | 
						|
    TestFixinProtocolFeesContract,
 | 
						|
    TestStakingContract,
 | 
						|
    TestWethContract,
 | 
						|
} from './wrappers';
 | 
						|
 | 
						|
blockchainTests.resets('ProtocolFees', env => {
 | 
						|
    const FEE_MULTIPLIER = 70e3;
 | 
						|
    let taker: string;
 | 
						|
    let unauthorized: string;
 | 
						|
    let protocolFees: TestFixinProtocolFeesContract;
 | 
						|
    let staking: TestStakingContract;
 | 
						|
    let weth: TestWethContract;
 | 
						|
    let feeCollectorController: FeeCollectorControllerContract;
 | 
						|
    let singleFeeAmount: BigNumber;
 | 
						|
 | 
						|
    before(async () => {
 | 
						|
        [taker, unauthorized] = await env.getAccountAddressesAsync();
 | 
						|
        weth = await TestWethContract.deployFrom0xArtifactAsync(
 | 
						|
            artifacts.TestWeth,
 | 
						|
            env.provider,
 | 
						|
            env.txDefaults,
 | 
						|
            artifacts,
 | 
						|
        );
 | 
						|
        staking = await TestStakingContract.deployFrom0xArtifactAsync(
 | 
						|
            artifacts.TestStaking,
 | 
						|
            env.provider,
 | 
						|
            env.txDefaults,
 | 
						|
            artifacts,
 | 
						|
            weth.address,
 | 
						|
        );
 | 
						|
        feeCollectorController = await FeeCollectorControllerContract.deployFrom0xArtifactAsync(
 | 
						|
            artifacts.FeeCollectorController,
 | 
						|
            env.provider,
 | 
						|
            env.txDefaults,
 | 
						|
            artifacts,
 | 
						|
            weth.address,
 | 
						|
            staking.address,
 | 
						|
        );
 | 
						|
        protocolFees = await TestFixinProtocolFeesContract.deployFrom0xArtifactAsync(
 | 
						|
            artifacts.TestFixinProtocolFees,
 | 
						|
            env.provider,
 | 
						|
            { ...env.txDefaults, from: taker },
 | 
						|
            artifacts,
 | 
						|
            weth.address,
 | 
						|
            staking.address,
 | 
						|
            feeCollectorController.address,
 | 
						|
            FEE_MULTIPLIER,
 | 
						|
        );
 | 
						|
        singleFeeAmount = await protocolFees.getSingleProtocolFee().callAsync();
 | 
						|
        await weth.mint(taker, singleFeeAmount).awaitTransactionSuccessAsync();
 | 
						|
        await weth.approve(protocolFees.address, singleFeeAmount).awaitTransactionSuccessAsync({ from: taker });
 | 
						|
    });
 | 
						|
 | 
						|
    describe('FeeCollector', () => {
 | 
						|
        it('should disallow unauthorized initialization', async () => {
 | 
						|
            const pool = hexUtils.random();
 | 
						|
 | 
						|
            await protocolFees.collectProtocolFee(pool).awaitTransactionSuccessAsync({ value: singleFeeAmount });
 | 
						|
            await protocolFees.transferFeesForPool(pool).awaitTransactionSuccessAsync();
 | 
						|
 | 
						|
            const feeCollector = new FeeCollectorContract(
 | 
						|
                await protocolFees.getFeeCollector(pool).callAsync(),
 | 
						|
                env.provider,
 | 
						|
                env.txDefaults,
 | 
						|
            );
 | 
						|
 | 
						|
            const tx = feeCollector
 | 
						|
                .initialize(weth.address, staking.address, pool)
 | 
						|
                .sendTransactionAsync({ from: unauthorized });
 | 
						|
            return expect(tx).to.revertWith(new AuthorizableRevertErrors.SenderNotAuthorizedError(unauthorized));
 | 
						|
        });
 | 
						|
    });
 | 
						|
 | 
						|
    describe('_collectProtocolFee()', () => {
 | 
						|
        const pool1 = hexUtils.random();
 | 
						|
        const pool2 = hexUtils.random();
 | 
						|
        let feeCollector1Address: string;
 | 
						|
        let feeCollector2Address: string;
 | 
						|
 | 
						|
        before(async () => {
 | 
						|
            feeCollector1Address = await protocolFees.getFeeCollector(pool1).callAsync();
 | 
						|
            feeCollector2Address = await protocolFees.getFeeCollector(pool2).callAsync();
 | 
						|
        });
 | 
						|
 | 
						|
        it('should revert if insufficient ETH transferred', async () => {
 | 
						|
            const tooLittle = singleFeeAmount.minus(1);
 | 
						|
            const tx = protocolFees.collectProtocolFee(pool1).awaitTransactionSuccessAsync({ value: tooLittle });
 | 
						|
            return expect(tx).to.revertWith('FixinProtocolFees/ETHER_TRANSFER_FALIED');
 | 
						|
        });
 | 
						|
 | 
						|
        it('should accept ETH fee', async () => {
 | 
						|
            const beforeETH = await env.web3Wrapper.getBalanceInWeiAsync(taker);
 | 
						|
            await protocolFees.collectProtocolFee(pool1).awaitTransactionSuccessAsync({ value: singleFeeAmount });
 | 
						|
            const afterETH = await env.web3Wrapper.getBalanceInWeiAsync(taker);
 | 
						|
 | 
						|
            // We check for greater than fee spent to allow for spending on gas.
 | 
						|
            await expect(beforeETH.minus(afterETH)).to.bignumber.gt(singleFeeAmount);
 | 
						|
 | 
						|
            await expect(await env.web3Wrapper.getBalanceInWeiAsync(feeCollector1Address)).to.bignumber.eq(
 | 
						|
                singleFeeAmount,
 | 
						|
            );
 | 
						|
        });
 | 
						|
 | 
						|
        it('should accept ETH after first transfer', async () => {
 | 
						|
            await protocolFees.collectProtocolFee(pool1).awaitTransactionSuccessAsync({ value: singleFeeAmount });
 | 
						|
            await protocolFees.transferFeesForPool(pool1).awaitTransactionSuccessAsync();
 | 
						|
            await protocolFees.collectProtocolFee(pool1).awaitTransactionSuccessAsync({ value: singleFeeAmount });
 | 
						|
            await protocolFees.transferFeesForPool(pool1).awaitTransactionSuccessAsync();
 | 
						|
 | 
						|
            const balanceWETH = await weth.balanceOf(staking.address).callAsync();
 | 
						|
 | 
						|
            // We leave 1 wei of WETH behind.
 | 
						|
            await expect(balanceWETH).to.bignumber.eq(singleFeeAmount.times(2).minus(1));
 | 
						|
            await expect(await weth.balanceOf(feeCollector1Address).callAsync()).to.bignumber.equal(1);
 | 
						|
            // And no ETH.
 | 
						|
            await expect(await env.web3Wrapper.getBalanceInWeiAsync(feeCollector1Address)).to.bignumber.eq(0);
 | 
						|
        });
 | 
						|
 | 
						|
        it('should attribute fees correctly', async () => {
 | 
						|
            await protocolFees.collectProtocolFee(pool1).awaitTransactionSuccessAsync({ value: singleFeeAmount });
 | 
						|
            await protocolFees.transferFeesForPool(pool1).awaitTransactionSuccessAsync();
 | 
						|
            await protocolFees.collectProtocolFee(pool2).awaitTransactionSuccessAsync({ value: singleFeeAmount });
 | 
						|
            await protocolFees.transferFeesForPool(pool2).awaitTransactionSuccessAsync();
 | 
						|
 | 
						|
            const pool1Balance = await staking.balanceForPool(pool1).callAsync();
 | 
						|
            const pool2Balance = await staking.balanceForPool(pool2).callAsync();
 | 
						|
 | 
						|
            const balanceWETH = await weth.balanceOf(staking.address).callAsync();
 | 
						|
 | 
						|
            await expect(balanceWETH).to.bignumber.equal(singleFeeAmount.times(2).minus(2));
 | 
						|
 | 
						|
            // We leave 1 wei of WETH behind.
 | 
						|
            await expect(pool1Balance).to.bignumber.equal(singleFeeAmount.minus(1));
 | 
						|
            await expect(pool2Balance).to.bignumber.equal(singleFeeAmount.minus(1));
 | 
						|
            await expect(await weth.balanceOf(feeCollector1Address).callAsync()).to.bignumber.equal(1);
 | 
						|
            await expect(await weth.balanceOf(feeCollector2Address).callAsync()).to.bignumber.equal(1);
 | 
						|
            await expect(pool2Balance).to.bignumber.equal(singleFeeAmount.minus(1));
 | 
						|
            // And no ETH.
 | 
						|
            await expect(await env.web3Wrapper.getBalanceInWeiAsync(feeCollector1Address)).to.bignumber.eq(0);
 | 
						|
            await expect(await env.web3Wrapper.getBalanceInWeiAsync(feeCollector2Address)).to.bignumber.eq(0);
 | 
						|
        });
 | 
						|
    });
 | 
						|
});
 |