protocol fee / staking integration tests (#40)

This commit is contained in:
Steve Marx 2020-11-20 09:19:59 -05:00 committed by GitHub
parent 018e25345b
commit b463a39bfa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 269 additions and 3 deletions

View File

@ -16,6 +16,7 @@
"quotes": ["error", "double"], "quotes": ["error", "double"],
"separate-by-one-line-in-contract": "error", "separate-by-one-line-in-contract": "error",
"space-after-comma": "error", "space-after-comma": "error",
"statement-indent": "error" "statement-indent": "error",
"no-empty-blocks": false
} }
} }

View File

@ -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)
{
}
}

View 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;
}
}

View File

@ -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 {}

View File

@ -38,7 +38,7 @@
}, },
"config": { "config": {
"publicInterfaceContracts": "TestFramework", "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." "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
}, },
"repository": { "repository": {

View File

@ -12,12 +12,15 @@ import * as TestContractWrapper from '../test/generated-artifacts/TestContractWr
import * as TestDydxUser from '../test/generated-artifacts/TestDydxUser.json'; import * as TestDydxUser from '../test/generated-artifacts/TestDydxUser.json';
import * as TestEth2Dai from '../test/generated-artifacts/TestEth2Dai.json'; import * as TestEth2Dai from '../test/generated-artifacts/TestEth2Dai.json';
import * as TestEth2DaiBridge from '../test/generated-artifacts/TestEth2DaiBridge.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 TestFramework from '../test/generated-artifacts/TestFramework.json';
import * as TestMainnetAggregatorFills from '../test/generated-artifacts/TestMainnetAggregatorFills.json'; import * as TestMainnetAggregatorFills from '../test/generated-artifacts/TestMainnetAggregatorFills.json';
import * as TestSignatureValidationWallet from '../test/generated-artifacts/TestSignatureValidationWallet.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 TestUniswapBridge from '../test/generated-artifacts/TestUniswapBridge.json';
import * as TestUniswapExchange from '../test/generated-artifacts/TestUniswapExchange.json'; import * as TestUniswapExchange from '../test/generated-artifacts/TestUniswapExchange.json';
import * as TestUniswapExchangeFactory from '../test/generated-artifacts/TestUniswapExchangeFactory.json'; import * as TestUniswapExchangeFactory from '../test/generated-artifacts/TestUniswapExchangeFactory.json';
import * as TestWethIntegration from '../test/generated-artifacts/TestWethIntegration.json';
export const artifacts = { export const artifacts = {
ChainlinkStopLimit: ChainlinkStopLimit as ContractArtifact, ChainlinkStopLimit: ChainlinkStopLimit as ContractArtifact,
IChainlinkAggregator: IChainlinkAggregator as ContractArtifact, IChainlinkAggregator: IChainlinkAggregator as ContractArtifact,
@ -26,10 +29,13 @@ export const artifacts = {
TestDydxUser: TestDydxUser as ContractArtifact, TestDydxUser: TestDydxUser as ContractArtifact,
TestEth2Dai: TestEth2Dai as ContractArtifact, TestEth2Dai: TestEth2Dai as ContractArtifact,
TestEth2DaiBridge: TestEth2DaiBridge as ContractArtifact, TestEth2DaiBridge: TestEth2DaiBridge as ContractArtifact,
TestFixinProtocolFeesIntegration: TestFixinProtocolFeesIntegration as ContractArtifact,
TestFramework: TestFramework as ContractArtifact, TestFramework: TestFramework as ContractArtifact,
TestMainnetAggregatorFills: TestMainnetAggregatorFills as ContractArtifact, TestMainnetAggregatorFills: TestMainnetAggregatorFills as ContractArtifact,
TestSignatureValidationWallet: TestSignatureValidationWallet as ContractArtifact, TestSignatureValidationWallet: TestSignatureValidationWallet as ContractArtifact,
TestStaking: TestStaking as ContractArtifact,
TestUniswapBridge: TestUniswapBridge as ContractArtifact, TestUniswapBridge: TestUniswapBridge as ContractArtifact,
TestUniswapExchange: TestUniswapExchange as ContractArtifact, TestUniswapExchange: TestUniswapExchange as ContractArtifact,
TestUniswapExchangeFactory: TestUniswapExchangeFactory as ContractArtifact, TestUniswapExchangeFactory: TestUniswapExchangeFactory as ContractArtifact,
TestWethIntegration: TestWethIntegration as ContractArtifact,
}; };

View File

@ -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_dydx_user';
export * from '../test/generated-wrappers/test_eth2_dai'; export * from '../test/generated-wrappers/test_eth2_dai';
export * from '../test/generated-wrappers/test_eth2_dai_bridge'; 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_framework';
export * from '../test/generated-wrappers/test_mainnet_aggregator_fills'; export * from '../test/generated-wrappers/test_mainnet_aggregator_fills';
export * from '../test/generated-wrappers/test_signature_validation_wallet'; 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_bridge';
export * from '../test/generated-wrappers/test_uniswap_exchange'; export * from '../test/generated-wrappers/test_uniswap_exchange';
export * from '../test/generated-wrappers/test_uniswap_exchange_factory'; export * from '../test/generated-wrappers/test_uniswap_exchange_factory';
export * from '../test/generated-wrappers/test_weth_integration';

View File

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

View File

@ -11,11 +11,14 @@
"test/generated-artifacts/TestDydxUser.json", "test/generated-artifacts/TestDydxUser.json",
"test/generated-artifacts/TestEth2Dai.json", "test/generated-artifacts/TestEth2Dai.json",
"test/generated-artifacts/TestEth2DaiBridge.json", "test/generated-artifacts/TestEth2DaiBridge.json",
"test/generated-artifacts/TestFixinProtocolFeesIntegration.json",
"test/generated-artifacts/TestFramework.json", "test/generated-artifacts/TestFramework.json",
"test/generated-artifacts/TestMainnetAggregatorFills.json", "test/generated-artifacts/TestMainnetAggregatorFills.json",
"test/generated-artifacts/TestSignatureValidationWallet.json", "test/generated-artifacts/TestSignatureValidationWallet.json",
"test/generated-artifacts/TestStaking.json",
"test/generated-artifacts/TestUniswapBridge.json", "test/generated-artifacts/TestUniswapBridge.json",
"test/generated-artifacts/TestUniswapExchange.json", "test/generated-artifacts/TestUniswapExchange.json",
"test/generated-artifacts/TestUniswapExchangeFactory.json" "test/generated-artifacts/TestUniswapExchangeFactory.json",
"test/generated-artifacts/TestWethIntegration.json"
] ]
} }