protocol fee / staking integration tests (#40)
This commit is contained in:
		@@ -16,6 +16,7 @@
 | 
			
		||||
        "quotes": ["error", "double"],
 | 
			
		||||
        "separate-by-one-line-in-contract": "error",
 | 
			
		||||
        "space-after-comma": "error",
 | 
			
		||||
        "statement-indent": "error"
 | 
			
		||||
        "statement-indent": "error",
 | 
			
		||||
        "no-empty-blocks": false
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,34 @@
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  Copyright 2020 ZeroEx Intl.
 | 
			
		||||
 | 
			
		||||
  Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
  you may not use this file except in compliance with the License.
 | 
			
		||||
  You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
  Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
  distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
  See the License for the specific language governing permissions and
 | 
			
		||||
  limitations under the License.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
pragma solidity ^0.6.5;
 | 
			
		||||
 | 
			
		||||
import "@0x/contracts-zero-ex/contracts/test/TestFixinProtocolFees.sol";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
contract TestFixinProtocolFeesIntegration is TestFixinProtocolFees {
 | 
			
		||||
    constructor(
 | 
			
		||||
        IEtherTokenV06 weth,
 | 
			
		||||
        IStaking staking,
 | 
			
		||||
        uint32 protocolFeeMultiplier
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        TestFixinProtocolFees(weth, staking, protocolFeeMultiplier)
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										110
									
								
								contracts/integrations/contracts/test/TestStaking.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								contracts/integrations/contracts/test/TestStaking.sol
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,110 @@
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  Copyright 2019 ZeroEx Intl.
 | 
			
		||||
 | 
			
		||||
  Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
  you may not use this file except in compliance with the License.
 | 
			
		||||
  You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
  Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
  distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
  See the License for the specific language governing permissions and
 | 
			
		||||
  limitations under the License.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
pragma solidity ^0.5.9;
 | 
			
		||||
pragma experimental ABIEncoderV2;
 | 
			
		||||
 | 
			
		||||
import "@0x/contracts-staking/contracts/src/Staking.sol";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
contract TestStaking is
 | 
			
		||||
    Staking
 | 
			
		||||
{
 | 
			
		||||
    IEtherToken public testWeth;
 | 
			
		||||
 | 
			
		||||
    struct TestPool {
 | 
			
		||||
        uint96 operatorStake;
 | 
			
		||||
        uint96 membersStake;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    mapping(bytes32 => TestPool) private _testPools;
 | 
			
		||||
 | 
			
		||||
    constructor(address exchangeAddress, IEtherToken _testWeth) public {
 | 
			
		||||
        testWeth = _testWeth;
 | 
			
		||||
 | 
			
		||||
        _addAuthorizedAddress(msg.sender);
 | 
			
		||||
        init();
 | 
			
		||||
        validExchanges[exchangeAddress] = true;
 | 
			
		||||
        _removeAuthorizedAddressAtIndex(msg.sender, 0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function advanceEpoch()
 | 
			
		||||
        external
 | 
			
		||||
    {
 | 
			
		||||
        currentEpoch += 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Create a test pool.
 | 
			
		||||
    function createTestPool(
 | 
			
		||||
        bytes32 poolId,
 | 
			
		||||
        uint96 operatorStake,
 | 
			
		||||
        uint96 membersStake
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
    {
 | 
			
		||||
        TestPool storage pool = _testPools[poolId];
 | 
			
		||||
        pool.operatorStake = operatorStake;
 | 
			
		||||
        pool.membersStake = membersStake;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function getAggregatedStatsForCurrentEpoch()
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (IStructs.AggregatedStats memory)
 | 
			
		||||
    {
 | 
			
		||||
        return aggregatedStatsByEpoch[currentEpoch];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Overridden to use test pools.
 | 
			
		||||
    function getTotalStakeDelegatedToPool(bytes32 poolId)
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (IStructs.StoredBalance memory balance)
 | 
			
		||||
    {
 | 
			
		||||
        TestPool memory pool = _testPools[poolId];
 | 
			
		||||
        uint96 stake = pool.operatorStake + pool.membersStake;
 | 
			
		||||
        return IStructs.StoredBalance({
 | 
			
		||||
            currentEpoch: currentEpoch.downcastToUint64(),
 | 
			
		||||
            currentEpochBalance: stake,
 | 
			
		||||
            nextEpochBalance: stake
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Overridden to use test pools.
 | 
			
		||||
    function getStakeDelegatedToPoolByOwner(address, bytes32 poolId)
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (IStructs.StoredBalance memory balance)
 | 
			
		||||
    {
 | 
			
		||||
        TestPool memory pool = _testPools[poolId];
 | 
			
		||||
        return IStructs.StoredBalance({
 | 
			
		||||
            currentEpoch: currentEpoch.downcastToUint64(),
 | 
			
		||||
            currentEpochBalance: pool.operatorStake,
 | 
			
		||||
            nextEpochBalance: pool.operatorStake
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function getWethContract()
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (IEtherToken wethContract)
 | 
			
		||||
    {
 | 
			
		||||
        return testWeth;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -0,0 +1,24 @@
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  Copyright 2020 ZeroEx Intl.
 | 
			
		||||
 | 
			
		||||
  Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
  you may not use this file except in compliance with the License.
 | 
			
		||||
  You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
  Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
  distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
  See the License for the specific language governing permissions and
 | 
			
		||||
  limitations under the License.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
pragma solidity ^0.6.5;
 | 
			
		||||
 | 
			
		||||
import "@0x/contracts-zero-ex/contracts/test/TestWeth.sol";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
contract TestWethIntegration is TestWeth {}
 | 
			
		||||
@@ -38,7 +38,7 @@
 | 
			
		||||
    },
 | 
			
		||||
    "config": {
 | 
			
		||||
        "publicInterfaceContracts": "TestFramework",
 | 
			
		||||
        "abis": "./test/generated-artifacts/@(ChainlinkStopLimit|IChainlinkAggregator|TestChainlinkAggregator|TestContractWrapper|TestDydxUser|TestEth2Dai|TestEth2DaiBridge|TestFramework|TestMainnetAggregatorFills|TestSignatureValidationWallet|TestUniswapBridge|TestUniswapExchange|TestUniswapExchangeFactory).json",
 | 
			
		||||
        "abis": "./test/generated-artifacts/@(ChainlinkStopLimit|IChainlinkAggregator|TestChainlinkAggregator|TestContractWrapper|TestDydxUser|TestEth2Dai|TestEth2DaiBridge|TestFixinProtocolFeesIntegration|TestFramework|TestMainnetAggregatorFills|TestSignatureValidationWallet|TestStaking|TestUniswapBridge|TestUniswapExchange|TestUniswapExchangeFactory|TestWethIntegration).json",
 | 
			
		||||
        "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
 | 
			
		||||
    },
 | 
			
		||||
    "repository": {
 | 
			
		||||
 
 | 
			
		||||
@@ -12,12 +12,15 @@ import * as TestContractWrapper from '../test/generated-artifacts/TestContractWr
 | 
			
		||||
import * as TestDydxUser from '../test/generated-artifacts/TestDydxUser.json';
 | 
			
		||||
import * as TestEth2Dai from '../test/generated-artifacts/TestEth2Dai.json';
 | 
			
		||||
import * as TestEth2DaiBridge from '../test/generated-artifacts/TestEth2DaiBridge.json';
 | 
			
		||||
import * as TestFixinProtocolFeesIntegration from '../test/generated-artifacts/TestFixinProtocolFeesIntegration.json';
 | 
			
		||||
import * as TestFramework from '../test/generated-artifacts/TestFramework.json';
 | 
			
		||||
import * as TestMainnetAggregatorFills from '../test/generated-artifacts/TestMainnetAggregatorFills.json';
 | 
			
		||||
import * as TestSignatureValidationWallet from '../test/generated-artifacts/TestSignatureValidationWallet.json';
 | 
			
		||||
import * as TestStaking from '../test/generated-artifacts/TestStaking.json';
 | 
			
		||||
import * as TestUniswapBridge from '../test/generated-artifacts/TestUniswapBridge.json';
 | 
			
		||||
import * as TestUniswapExchange from '../test/generated-artifacts/TestUniswapExchange.json';
 | 
			
		||||
import * as TestUniswapExchangeFactory from '../test/generated-artifacts/TestUniswapExchangeFactory.json';
 | 
			
		||||
import * as TestWethIntegration from '../test/generated-artifacts/TestWethIntegration.json';
 | 
			
		||||
export const artifacts = {
 | 
			
		||||
    ChainlinkStopLimit: ChainlinkStopLimit as ContractArtifact,
 | 
			
		||||
    IChainlinkAggregator: IChainlinkAggregator as ContractArtifact,
 | 
			
		||||
@@ -26,10 +29,13 @@ export const artifacts = {
 | 
			
		||||
    TestDydxUser: TestDydxUser as ContractArtifact,
 | 
			
		||||
    TestEth2Dai: TestEth2Dai as ContractArtifact,
 | 
			
		||||
    TestEth2DaiBridge: TestEth2DaiBridge as ContractArtifact,
 | 
			
		||||
    TestFixinProtocolFeesIntegration: TestFixinProtocolFeesIntegration as ContractArtifact,
 | 
			
		||||
    TestFramework: TestFramework as ContractArtifact,
 | 
			
		||||
    TestMainnetAggregatorFills: TestMainnetAggregatorFills as ContractArtifact,
 | 
			
		||||
    TestSignatureValidationWallet: TestSignatureValidationWallet as ContractArtifact,
 | 
			
		||||
    TestStaking: TestStaking as ContractArtifact,
 | 
			
		||||
    TestUniswapBridge: TestUniswapBridge as ContractArtifact,
 | 
			
		||||
    TestUniswapExchange: TestUniswapExchange as ContractArtifact,
 | 
			
		||||
    TestUniswapExchangeFactory: TestUniswapExchangeFactory as ContractArtifact,
 | 
			
		||||
    TestWethIntegration: TestWethIntegration as ContractArtifact,
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -10,9 +10,12 @@ export * from '../test/generated-wrappers/test_contract_wrapper';
 | 
			
		||||
export * from '../test/generated-wrappers/test_dydx_user';
 | 
			
		||||
export * from '../test/generated-wrappers/test_eth2_dai';
 | 
			
		||||
export * from '../test/generated-wrappers/test_eth2_dai_bridge';
 | 
			
		||||
export * from '../test/generated-wrappers/test_fixin_protocol_fees_integration';
 | 
			
		||||
export * from '../test/generated-wrappers/test_framework';
 | 
			
		||||
export * from '../test/generated-wrappers/test_mainnet_aggregator_fills';
 | 
			
		||||
export * from '../test/generated-wrappers/test_signature_validation_wallet';
 | 
			
		||||
export * from '../test/generated-wrappers/test_staking';
 | 
			
		||||
export * from '../test/generated-wrappers/test_uniswap_bridge';
 | 
			
		||||
export * from '../test/generated-wrappers/test_uniswap_exchange';
 | 
			
		||||
export * from '../test/generated-wrappers/test_uniswap_exchange_factory';
 | 
			
		||||
export * from '../test/generated-wrappers/test_weth_integration';
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,85 @@
 | 
			
		||||
import { blockchainTests, constants, expect } from '@0x/contracts-test-utils';
 | 
			
		||||
import { BigNumber, hexUtils } from '@0x/utils';
 | 
			
		||||
 | 
			
		||||
import { artifacts } from '../artifacts';
 | 
			
		||||
import {
 | 
			
		||||
    TestFixinProtocolFeesIntegrationContract,
 | 
			
		||||
    TestStakingContract,
 | 
			
		||||
    TestWethIntegrationContract,
 | 
			
		||||
} from '../wrappers';
 | 
			
		||||
 | 
			
		||||
blockchainTests.resets('ProtocolFeeIntegration', env => {
 | 
			
		||||
    const FEE_MULTIPLIER = 70e3;
 | 
			
		||||
    let owner: string;
 | 
			
		||||
    let taker: string;
 | 
			
		||||
    let protocolFees: TestFixinProtocolFeesIntegrationContract;
 | 
			
		||||
    let staking: TestStakingContract;
 | 
			
		||||
    let weth: TestWethIntegrationContract;
 | 
			
		||||
    let singleFeeAmount: BigNumber;
 | 
			
		||||
 | 
			
		||||
    before(async () => {
 | 
			
		||||
        [owner, taker] = await env.getAccountAddressesAsync();
 | 
			
		||||
        weth = await TestWethIntegrationContract.deployFrom0xArtifactAsync(
 | 
			
		||||
            artifacts.TestWethIntegration,
 | 
			
		||||
            env.provider,
 | 
			
		||||
            env.txDefaults,
 | 
			
		||||
            artifacts,
 | 
			
		||||
        );
 | 
			
		||||
        staking = await TestStakingContract.deployFrom0xArtifactAsync(
 | 
			
		||||
            artifacts.TestStaking,
 | 
			
		||||
            env.provider,
 | 
			
		||||
            env.txDefaults,
 | 
			
		||||
            artifacts,
 | 
			
		||||
            constants.NULL_ADDRESS, // exchange address, which we don't know yet
 | 
			
		||||
            weth.address,
 | 
			
		||||
        );
 | 
			
		||||
        protocolFees = await TestFixinProtocolFeesIntegrationContract.deployFrom0xArtifactAsync(
 | 
			
		||||
            artifacts.TestFixinProtocolFeesIntegration,
 | 
			
		||||
            env.provider,
 | 
			
		||||
            { ...env.txDefaults, from: taker },
 | 
			
		||||
            artifacts,
 | 
			
		||||
            weth.address,
 | 
			
		||||
            staking.address,
 | 
			
		||||
            FEE_MULTIPLIER,
 | 
			
		||||
        );
 | 
			
		||||
        await staking.addAuthorizedAddress(owner).awaitTransactionSuccessAsync();
 | 
			
		||||
        await staking.addExchangeAddress(protocolFees.address).awaitTransactionSuccessAsync({ from: owner });
 | 
			
		||||
        await weth.mint(taker, constants.ONE_ETHER).awaitTransactionSuccessAsync();
 | 
			
		||||
        await weth.approve(protocolFees.address, constants.ONE_ETHER).awaitTransactionSuccessAsync({ from: taker });
 | 
			
		||||
 | 
			
		||||
        singleFeeAmount = await protocolFees.getSingleProtocolFee().callAsync();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    describe('fee collection integration', () => {
 | 
			
		||||
        const pool0 = constants.NULL_BYTES32;
 | 
			
		||||
        const poolId = hexUtils.random();
 | 
			
		||||
 | 
			
		||||
        it('should collect fees for pool 0', async () => {
 | 
			
		||||
            await protocolFees.collectProtocolFee(pool0).awaitTransactionSuccessAsync({ value: singleFeeAmount });
 | 
			
		||||
            await protocolFees.transferFeesForPool(pool0).awaitTransactionSuccessAsync();
 | 
			
		||||
 | 
			
		||||
            // Fees in the pool bytes32(0) don't get attributed to a pool.
 | 
			
		||||
            await expect(
 | 
			
		||||
                (await staking.getStakingPoolStatsThisEpoch(pool0).callAsync()).feesCollected,
 | 
			
		||||
            ).to.bignumber.equal(constants.ZERO_AMOUNT);
 | 
			
		||||
 | 
			
		||||
            // Expected amount is singleFeeAmount - 1 because we leave 1 wei of WETH behind for future gas savings.
 | 
			
		||||
            return expect(await weth.balanceOf(staking.address).callAsync()).to.bignumber.equal(
 | 
			
		||||
                singleFeeAmount.minus(1),
 | 
			
		||||
            );
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('should collect fees for non-zero pool', async () => {
 | 
			
		||||
            const eth100 = constants.ONE_ETHER.multipliedBy(100);
 | 
			
		||||
            await staking.createTestPool(poolId, eth100, eth100).awaitTransactionSuccessAsync();
 | 
			
		||||
 | 
			
		||||
            await protocolFees.collectProtocolFee(poolId).awaitTransactionSuccessAsync({ value: singleFeeAmount });
 | 
			
		||||
            await protocolFees.transferFeesForPool(poolId).awaitTransactionSuccessAsync();
 | 
			
		||||
 | 
			
		||||
            // Expected amount is singleFeeAmount - 1 because we leave 1 wei of WETH behind for future gas savings.
 | 
			
		||||
            return expect(
 | 
			
		||||
                (await staking.getStakingPoolStatsThisEpoch(poolId).callAsync()).feesCollected,
 | 
			
		||||
            ).to.bignumber.equal(singleFeeAmount.minus(1));
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
@@ -11,11 +11,14 @@
 | 
			
		||||
        "test/generated-artifacts/TestDydxUser.json",
 | 
			
		||||
        "test/generated-artifacts/TestEth2Dai.json",
 | 
			
		||||
        "test/generated-artifacts/TestEth2DaiBridge.json",
 | 
			
		||||
        "test/generated-artifacts/TestFixinProtocolFeesIntegration.json",
 | 
			
		||||
        "test/generated-artifacts/TestFramework.json",
 | 
			
		||||
        "test/generated-artifacts/TestMainnetAggregatorFills.json",
 | 
			
		||||
        "test/generated-artifacts/TestSignatureValidationWallet.json",
 | 
			
		||||
        "test/generated-artifacts/TestStaking.json",
 | 
			
		||||
        "test/generated-artifacts/TestUniswapBridge.json",
 | 
			
		||||
        "test/generated-artifacts/TestUniswapExchange.json",
 | 
			
		||||
        "test/generated-artifacts/TestUniswapExchangeFactory.json"
 | 
			
		||||
        "test/generated-artifacts/TestUniswapExchangeFactory.json",
 | 
			
		||||
        "test/generated-artifacts/TestWethIntegration.json"
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user