* `@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);
 | |
|         });
 | |
|     });
 | |
| });
 |