129 lines
5.8 KiB
TypeScript
129 lines
5.8 KiB
TypeScript
import { blockchainTests } from '@0x/contracts-test-utils';
|
|
import * as _ from 'lodash';
|
|
|
|
import { Actor } from '../framework/actors/base';
|
|
import {
|
|
MakerTaker,
|
|
OperatorStakerMaker,
|
|
StakerKeeper,
|
|
StakerMaker,
|
|
StakerOperator,
|
|
} from '../framework/actors/hybrids';
|
|
import { Keeper } from '../framework/actors/keeper';
|
|
import { Maker } from '../framework/actors/maker';
|
|
import { PoolOperator } from '../framework/actors/pool_operator';
|
|
import { Staker } from '../framework/actors/staker';
|
|
import { Taker } from '../framework/actors/taker';
|
|
import { filterActorsByRole } from '../framework/actors/utils';
|
|
import { AssertionResult } from '../framework/assertions/function_assertion';
|
|
import { BlockchainBalanceStore } from '../framework/balances/blockchain_balance_store';
|
|
import { DeploymentManager } from '../framework/deployment_manager';
|
|
import { Simulation, SimulationEnvironment } from '../framework/simulation';
|
|
import { Pseudorandom } from '../framework/utils/pseudorandom';
|
|
|
|
import { PoolMembershipSimulation } from './pool_membership_test';
|
|
import { StakeManagementSimulation } from './stake_management_test';
|
|
|
|
export class StakingRewardsSimulation extends Simulation {
|
|
protected async *_assertionGenerator(): AsyncIterableIterator<AssertionResult | void> {
|
|
const { actors } = this.environment;
|
|
const stakers = filterActorsByRole(actors, Staker);
|
|
const keepers = filterActorsByRole(actors, Keeper);
|
|
|
|
const poolMembership = new PoolMembershipSimulation(this.environment);
|
|
const stakeManagement = new StakeManagementSimulation(this.environment);
|
|
|
|
const [actions, weights] = _.unzip([
|
|
// 10% chance of executing validWithdrawDelegatorRewards for a random staker
|
|
...stakers.map(staker => [staker.simulationActions.validWithdrawDelegatorRewards, 0.1 / stakers.length]),
|
|
// 10% chance of executing validFinalizePool for a random keeper
|
|
...keepers.map(keeper => [keeper.simulationActions.validFinalizePool, 0.1 / keepers.length]),
|
|
// 7% chance of executing validEndEpoch for a random keeper
|
|
...keepers.map(keeper => [keeper.simulationActions.validEndEpoch, 0.07 / keepers.length]),
|
|
// 3% chance of executing invalidEndEpoch for a random keeper
|
|
...keepers.map(keeper => [keeper.simulationActions.invalidEndEpoch, 0.03 / keepers.length]),
|
|
// 50% chance of executing an assertion generated from the pool membership simulation
|
|
[poolMembership.generator, 0.5],
|
|
// 20% chance of executing an assertion generated from the stake management simulation
|
|
[stakeManagement.generator, 0.2],
|
|
]) as [Array<AsyncIterableIterator<AssertionResult | void>>, number[]];
|
|
while (true) {
|
|
const action = Pseudorandom.sample(actions, weights);
|
|
yield (await action!.next()).value; // tslint:disable-line:no-non-null-assertion
|
|
}
|
|
}
|
|
}
|
|
|
|
blockchainTests('Staking rewards fuzz test', env => {
|
|
before(function(): void {
|
|
if (process.env.FUZZ_TEST !== 'staking_rewards') {
|
|
this.skip();
|
|
}
|
|
});
|
|
|
|
after(async () => {
|
|
Actor.reset();
|
|
});
|
|
|
|
it('fuzz', async () => {
|
|
// Deploy contracts
|
|
const deployment = await DeploymentManager.deployAsync(env, {
|
|
numErc20TokensToDeploy: 4,
|
|
numErc721TokensToDeploy: 0,
|
|
numErc1155TokensToDeploy: 0,
|
|
});
|
|
const [ERC20TokenA, ERC20TokenB, ERC20TokenC, ERC20TokenD] = deployment.tokens.erc20;
|
|
|
|
// Set up balance store
|
|
const balanceStore = new BlockchainBalanceStore(
|
|
{
|
|
StakingProxy: deployment.staking.stakingProxy.address,
|
|
ZRXVault: deployment.staking.zrxVault.address,
|
|
},
|
|
{
|
|
erc20: {
|
|
ZRX: deployment.tokens.zrx,
|
|
WETH: deployment.tokens.weth,
|
|
ERC20TokenA,
|
|
ERC20TokenB,
|
|
ERC20TokenC,
|
|
ERC20TokenD,
|
|
},
|
|
},
|
|
);
|
|
|
|
// Spin up actors
|
|
const actors = [
|
|
new Maker({ deployment, name: 'Maker 1' }),
|
|
new Maker({ deployment, name: 'Maker 2' }),
|
|
new Taker({ deployment, name: 'Taker 1' }),
|
|
new Taker({ deployment, name: 'Taker 2' }),
|
|
new MakerTaker({ deployment, name: 'Maker/Taker' }),
|
|
new Staker({ deployment, name: 'Staker 1' }),
|
|
new Staker({ deployment, name: 'Staker 2' }),
|
|
new Keeper({ deployment, name: 'Keeper' }),
|
|
new StakerKeeper({ deployment, name: 'Staker/Keeper' }),
|
|
new StakerMaker({ deployment, name: 'Staker/Maker' }),
|
|
new PoolOperator({ deployment, name: 'Pool Operator' }),
|
|
new StakerOperator({ deployment, name: 'Staker/Operator' }),
|
|
new OperatorStakerMaker({ deployment, name: 'Operator/Staker/Maker' }),
|
|
];
|
|
|
|
// Set up simulation environment
|
|
const simulationEnvironment = new SimulationEnvironment(deployment, balanceStore, actors);
|
|
|
|
// Takers need to set a WETH allowance for the staking proxy in case they pay the protocol fee in WETH
|
|
const takers = filterActorsByRole(actors, Taker);
|
|
for (const taker of takers) {
|
|
await taker.configureERC20TokenAsync(deployment.tokens.weth, deployment.staking.stakingProxy.address);
|
|
}
|
|
// Stakers need to set a ZRX allowance to deposit their ZRX into the zrxVault
|
|
const stakers = filterActorsByRole(actors, Staker);
|
|
for (const staker of stakers) {
|
|
await staker.configureERC20TokenAsync(deployment.tokens.zrx);
|
|
}
|
|
const simulation = new StakingRewardsSimulation(simulationEnvironment);
|
|
return simulation.fuzzAsync();
|
|
});
|
|
});
|