add/update comments
This commit is contained in:
parent
1fde62eeb6
commit
865a2b1fb0
@ -132,9 +132,8 @@ export const LibFractions = {
|
|||||||
maxValue: BigNumber = new BigNumber(2 ** 127),
|
maxValue: BigNumber = new BigNumber(2 ** 127),
|
||||||
): [BigNumber, BigNumber] => {
|
): [BigNumber, BigNumber] => {
|
||||||
if (numerator.isGreaterThan(maxValue) || denominator.isGreaterThan(maxValue)) {
|
if (numerator.isGreaterThan(maxValue) || denominator.isGreaterThan(maxValue)) {
|
||||||
const rescaleBase = numerator.isGreaterThanOrEqualTo(denominator)
|
let rescaleBase = numerator.isGreaterThanOrEqualTo(denominator) ? numerator : denominator;
|
||||||
? safeDiv(numerator, maxValue)
|
rescaleBase = safeDiv(rescaleBase, maxValue);
|
||||||
: safeDiv(denominator, maxValue);
|
|
||||||
return [safeDiv(numerator, rescaleBase), safeDiv(denominator, rescaleBase)];
|
return [safeDiv(numerator, rescaleBase), safeDiv(denominator, rescaleBase)];
|
||||||
} else {
|
} else {
|
||||||
return [numerator, denominator];
|
return [numerator, denominator];
|
||||||
|
@ -89,6 +89,7 @@ export function KeeperMixin<TBase extends Constructor>(Base: TBase): TBase & Con
|
|||||||
const { stakingPools } = this.actor.simulationEnvironment!;
|
const { stakingPools } = this.actor.simulationEnvironment!;
|
||||||
const assertion = validFinalizePoolAssertion(this.actor.deployment, this.actor.simulationEnvironment!);
|
const assertion = validFinalizePoolAssertion(this.actor.deployment, this.actor.simulationEnvironment!);
|
||||||
while (true) {
|
while (true) {
|
||||||
|
// Finalize a random pool, or do nothing if there are no pools in the simulation yet.
|
||||||
const poolId = Pseudorandom.sample(Object.keys(stakingPools));
|
const poolId = Pseudorandom.sample(Object.keys(stakingPools));
|
||||||
if (poolId === undefined) {
|
if (poolId === undefined) {
|
||||||
yield;
|
yield;
|
||||||
|
@ -107,22 +107,29 @@ export function StakerMixin<TBase extends Constructor>(Base: TBase): TBase & Con
|
|||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const { currentEpoch } = this.actor.simulationEnvironment!;
|
const { currentEpoch } = this.actor.simulationEnvironment!;
|
||||||
|
// Pick a random pool that this staker has delegated to (undefined if no such pools exist)
|
||||||
const fromPoolId = Pseudorandom.sample(
|
const fromPoolId = Pseudorandom.sample(
|
||||||
Object.keys(_.omit(this.stake[StakeStatus.Delegated], ['total'])),
|
Object.keys(_.omit(this.stake[StakeStatus.Delegated], ['total'])),
|
||||||
);
|
);
|
||||||
|
// The `from` status must be Undelegated if the staker isn't delegated to any pools
|
||||||
|
// at the moment, or if the chosen pool is unfinalized
|
||||||
const fromStatus =
|
const fromStatus =
|
||||||
fromPoolId === undefined || stakingPools[fromPoolId].lastFinalized.isLessThan(currentEpoch.minus(1))
|
fromPoolId === undefined || stakingPools[fromPoolId].lastFinalized.isLessThan(currentEpoch.minus(1))
|
||||||
? StakeStatus.Undelegated
|
? StakeStatus.Undelegated
|
||||||
: (Pseudorandom.sample([StakeStatus.Undelegated, StakeStatus.Delegated]) as StakeStatus);
|
: (Pseudorandom.sample([StakeStatus.Undelegated, StakeStatus.Delegated]) as StakeStatus);
|
||||||
const from = new StakeInfo(fromStatus, fromPoolId);
|
const from = new StakeInfo(fromStatus, fromPoolId);
|
||||||
|
|
||||||
|
// Pick a random pool to move the stake to
|
||||||
const toPoolId = Pseudorandom.sample(Object.keys(stakingPools));
|
const toPoolId = Pseudorandom.sample(Object.keys(stakingPools));
|
||||||
|
// The `from` status must be Undelegated if no pools exist in the simulation yet,
|
||||||
|
// or if the chosen pool is unfinalized
|
||||||
const toStatus =
|
const toStatus =
|
||||||
toPoolId === undefined || stakingPools[toPoolId].lastFinalized.isLessThan(currentEpoch.minus(1))
|
toPoolId === undefined || stakingPools[toPoolId].lastFinalized.isLessThan(currentEpoch.minus(1))
|
||||||
? StakeStatus.Undelegated
|
? StakeStatus.Undelegated
|
||||||
: (Pseudorandom.sample([StakeStatus.Undelegated, StakeStatus.Delegated]) as StakeStatus);
|
: (Pseudorandom.sample([StakeStatus.Undelegated, StakeStatus.Delegated]) as StakeStatus);
|
||||||
const to = new StakeInfo(toStatus, toPoolId);
|
const to = new StakeInfo(toStatus, toPoolId);
|
||||||
|
|
||||||
|
// The next epoch balance of the `from` stake is the amount that can be moved
|
||||||
const moveableStake =
|
const moveableStake =
|
||||||
from.status === StakeStatus.Undelegated
|
from.status === StakeStatus.Undelegated
|
||||||
? this.stake[StakeStatus.Undelegated].nextEpochBalance
|
? this.stake[StakeStatus.Undelegated].nextEpochBalance
|
||||||
@ -141,6 +148,7 @@ export function StakerMixin<TBase extends Constructor>(Base: TBase): TBase & Con
|
|||||||
);
|
);
|
||||||
while (true) {
|
while (true) {
|
||||||
const prevEpoch = this.actor.simulationEnvironment!.currentEpoch.minus(1);
|
const prevEpoch = this.actor.simulationEnvironment!.currentEpoch.minus(1);
|
||||||
|
// Pick a finalized pool
|
||||||
const poolId = Pseudorandom.sample(
|
const poolId = Pseudorandom.sample(
|
||||||
Object.keys(stakingPools).filter(id =>
|
Object.keys(stakingPools).filter(id =>
|
||||||
stakingPools[id].lastFinalized.isGreaterThanOrEqualTo(prevEpoch),
|
stakingPools[id].lastFinalized.isGreaterThanOrEqualTo(prevEpoch),
|
||||||
|
@ -68,37 +68,36 @@ export function TakerMixin<TBase extends Constructor>(Base: TBase): TBase & Cons
|
|||||||
const { actors, balanceStore } = this.actor.simulationEnvironment!;
|
const { actors, balanceStore } = this.actor.simulationEnvironment!;
|
||||||
const assertion = validFillOrderAssertion(this.actor.deployment, this.actor.simulationEnvironment!);
|
const assertion = validFillOrderAssertion(this.actor.deployment, this.actor.simulationEnvironment!);
|
||||||
while (true) {
|
while (true) {
|
||||||
|
// Choose a maker to be the other side of the order
|
||||||
const maker = Pseudorandom.sample(filterActorsByRole(actors, Maker));
|
const maker = Pseudorandom.sample(filterActorsByRole(actors, Maker));
|
||||||
if (maker === undefined) {
|
if (maker === undefined) {
|
||||||
yield;
|
yield;
|
||||||
} else {
|
} else {
|
||||||
await balanceStore.updateErc20BalancesAsync();
|
await balanceStore.updateErc20BalancesAsync();
|
||||||
|
// Choose the assets for the order
|
||||||
const [makerToken, makerFeeToken, takerToken, takerFeeToken] = Pseudorandom.sampleSize(
|
const [makerToken, makerFeeToken, takerToken, takerFeeToken] = Pseudorandom.sampleSize(
|
||||||
this.actor.deployment.tokens.erc20,
|
this.actor.deployment.tokens.erc20,
|
||||||
4, // tslint:disable-line:custom-no-magic-numbers
|
4, // tslint:disable-line:custom-no-magic-numbers
|
||||||
);
|
);
|
||||||
|
|
||||||
const configureOrderAssetAsync = async (
|
// Maker and taker set balances/allowances to guarantee that the fill succeeds.
|
||||||
owner: Actor,
|
// Amounts are chosen to be within each actor's balance (divided by 2, in case
|
||||||
token: DummyERC20TokenContract,
|
// e.g. makerAsset = makerFeeAsset)
|
||||||
): Promise<BigNumber> => {
|
|
||||||
let balance = balanceStore.balances.erc20[owner.address][token.address];
|
|
||||||
await owner.configureERC20TokenAsync(token);
|
|
||||||
balance = balanceStore.balances.erc20[owner.address][token.address] =
|
|
||||||
constants.INITIAL_ERC20_BALANCE;
|
|
||||||
return Pseudorandom.integer(balance.dividedToIntegerBy(2));
|
|
||||||
};
|
|
||||||
|
|
||||||
const [makerAssetAmount, makerFee, takerAssetAmount, takerFee] = await Promise.all(
|
const [makerAssetAmount, makerFee, takerAssetAmount, takerFee] = await Promise.all(
|
||||||
[
|
[
|
||||||
[maker, makerToken],
|
[maker, makerToken],
|
||||||
[maker, makerFeeToken],
|
[maker, makerFeeToken],
|
||||||
[this.actor, takerToken],
|
[this.actor, takerToken],
|
||||||
[this.actor, takerFeeToken],
|
[this.actor, takerFeeToken],
|
||||||
].map(async ([owner, token]) =>
|
].map(async ([owner, token]) => {
|
||||||
configureOrderAssetAsync(owner as Actor, token as DummyERC20TokenContract),
|
let balance = balanceStore.balances.erc20[owner.address][token.address];
|
||||||
),
|
await (owner as Actor).configureERC20TokenAsync(token as DummyERC20TokenContract);
|
||||||
|
balance = balanceStore.balances.erc20[owner.address][token.address] =
|
||||||
|
constants.INITIAL_ERC20_BALANCE;
|
||||||
|
return Pseudorandom.integer(balance.dividedToIntegerBy(2));
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
|
// Encode asset data
|
||||||
const [makerAssetData, makerFeeAssetData, takerAssetData, takerFeeAssetData] = [
|
const [makerAssetData, makerFeeAssetData, takerAssetData, takerFeeAssetData] = [
|
||||||
makerToken,
|
makerToken,
|
||||||
makerFeeToken,
|
makerFeeToken,
|
||||||
@ -108,6 +107,7 @@ export function TakerMixin<TBase extends Constructor>(Base: TBase): TBase & Cons
|
|||||||
this.actor.deployment.assetDataEncoder.ERC20Token(token.address).getABIEncodedTransactionData(),
|
this.actor.deployment.assetDataEncoder.ERC20Token(token.address).getABIEncodedTransactionData(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Maker signs the order
|
||||||
const order = await maker.signOrderAsync({
|
const order = await maker.signOrderAsync({
|
||||||
makerAssetData,
|
makerAssetData,
|
||||||
takerAssetData,
|
takerAssetData,
|
||||||
@ -120,7 +120,10 @@ export function TakerMixin<TBase extends Constructor>(Base: TBase): TBase & Cons
|
|||||||
feeRecipientAddress: Pseudorandom.sample(actors)!.address,
|
feeRecipientAddress: Pseudorandom.sample(actors)!.address,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Taker fills the order by a random amount (up to the order's takerAssetAmount)
|
||||||
const fillAmount = Pseudorandom.integer(order.takerAssetAmount);
|
const fillAmount = Pseudorandom.integer(order.takerAssetAmount);
|
||||||
|
// Taker executes the fill with a random msg.value, so that sometimes the
|
||||||
|
// protocol fee is paid in ETH and other times it's paid in WETH.
|
||||||
yield assertion.executeAsync([order, fillAmount, order.signature], {
|
yield assertion.executeAsync([order, fillAmount, order.signature], {
|
||||||
from: this.actor.address,
|
from: this.actor.address,
|
||||||
value: Pseudorandom.integer(DeploymentManager.protocolFee.times(2)),
|
value: Pseudorandom.integer(DeploymentManager.protocolFee.times(2)),
|
||||||
|
@ -21,11 +21,10 @@ interface EndEpochBeforeInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a FunctionAssertion for `stake` which assumes valid input is provided. The
|
* Returns a FunctionAssertion for `endEpoch` which assumes valid input is provided. It checks
|
||||||
* FunctionAssertion checks that the staker and zrxVault's balances of ZRX decrease and increase,
|
* that the staking proxy contract wrapped its ETH balance, aggregated stats were updated, and
|
||||||
* respectively, by the input amount.
|
* EpochFinalized/EpochEnded events were emitted.
|
||||||
*/
|
*/
|
||||||
/* tslint:disable:no-unnecessary-type-assertion */
|
|
||||||
export function validEndEpochAssertion(
|
export function validEndEpochAssertion(
|
||||||
deployment: DeploymentManager,
|
deployment: DeploymentManager,
|
||||||
simulationEnvironment: SimulationEnvironment,
|
simulationEnvironment: SimulationEnvironment,
|
||||||
@ -47,7 +46,7 @@ export function validEndEpochAssertion(
|
|||||||
expect(result.success, `Error: ${result.data}`).to.be.true();
|
expect(result.success, `Error: ${result.data}`).to.be.true();
|
||||||
|
|
||||||
const { currentEpoch } = simulationEnvironment;
|
const { currentEpoch } = simulationEnvironment;
|
||||||
const logs = result.receipt!.logs; // tslint:disable-line:no-non-null-assertion
|
const logs = result.receipt!.logs; // tslint:disable-line
|
||||||
|
|
||||||
// Check WETH deposit event
|
// Check WETH deposit event
|
||||||
const previousEthBalance = balanceStore.balances.eth[stakingWrapper.address] || constants.ZERO_AMOUNT;
|
const previousEthBalance = balanceStore.balances.eth[stakingWrapper.address] || constants.ZERO_AMOUNT;
|
||||||
@ -61,6 +60,7 @@ export function validEndEpochAssertion(
|
|||||||
: [];
|
: [];
|
||||||
verifyEventsFromLogs<WETH9DepositEventArgs>(logs, expectedDepositEvents, WETH9Events.Deposit);
|
verifyEventsFromLogs<WETH9DepositEventArgs>(logs, expectedDepositEvents, WETH9Events.Deposit);
|
||||||
|
|
||||||
|
// Check that the aggregated stats were updated
|
||||||
await balanceStore.updateErc20BalancesAsync();
|
await balanceStore.updateErc20BalancesAsync();
|
||||||
const { wethReservedForPoolRewards, aggregatedStatsBefore } = beforeInfo;
|
const { wethReservedForPoolRewards, aggregatedStatsBefore } = beforeInfo;
|
||||||
const expectedAggregatedStats = {
|
const expectedAggregatedStats = {
|
||||||
@ -71,12 +71,12 @@ export function validEndEpochAssertion(
|
|||||||
constants.ZERO_AMOUNT,
|
constants.ZERO_AMOUNT,
|
||||||
).minus(wethReservedForPoolRewards),
|
).minus(wethReservedForPoolRewards),
|
||||||
};
|
};
|
||||||
|
|
||||||
const aggregatedStatsAfter = AggregatedStats.fromArray(
|
const aggregatedStatsAfter = AggregatedStats.fromArray(
|
||||||
await stakingWrapper.aggregatedStatsByEpoch(currentEpoch).callAsync(),
|
await stakingWrapper.aggregatedStatsByEpoch(currentEpoch).callAsync(),
|
||||||
);
|
);
|
||||||
expect(aggregatedStatsAfter).to.deep.equal(expectedAggregatedStats);
|
expect(aggregatedStatsAfter).to.deep.equal(expectedAggregatedStats);
|
||||||
|
|
||||||
|
// Check that an EpochEnded event was emitted
|
||||||
verifyEventsFromLogs<StakingEpochEndedEventArgs>(
|
verifyEventsFromLogs<StakingEpochEndedEventArgs>(
|
||||||
logs,
|
logs,
|
||||||
[
|
[
|
||||||
@ -91,6 +91,7 @@ export function validEndEpochAssertion(
|
|||||||
StakingEvents.EpochEnded,
|
StakingEvents.EpochEnded,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// If there are no more pools to finalize, an EpochFinalized event should've been emitted
|
||||||
const expectedEpochFinalizedEvents = aggregatedStatsAfter.numPoolsToFinalize.isZero()
|
const expectedEpochFinalizedEvents = aggregatedStatsAfter.numPoolsToFinalize.isZero()
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
@ -106,12 +107,13 @@ export function validEndEpochAssertion(
|
|||||||
StakingEvents.EpochFinalized,
|
StakingEvents.EpochFinalized,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// The function returns the remaining number of unfinalized pools for the epoch
|
||||||
expect(result.data, 'endEpoch should return the number of unfinalized pools').to.bignumber.equal(
|
expect(result.data, 'endEpoch should return the number of unfinalized pools').to.bignumber.equal(
|
||||||
aggregatedStatsAfter.numPoolsToFinalize,
|
aggregatedStatsAfter.numPoolsToFinalize,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Update currentEpoch locally
|
||||||
simulationEnvironment.currentEpoch = currentEpoch.plus(1);
|
simulationEnvironment.currentEpoch = currentEpoch.plus(1);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
/* tslint:enable:no-unnecessary-type-assertion */
|
|
||||||
|
@ -155,6 +155,7 @@ export function validFillOrderAssertion(
|
|||||||
// Ensure that the correct events were emitted.
|
// Ensure that the correct events were emitted.
|
||||||
verifyFillEvents(txData, order, result.receipt!, deployment, fillAmount);
|
verifyFillEvents(txData, order, result.receipt!, deployment, fillAmount);
|
||||||
|
|
||||||
|
// If the maker is not in a staking pool, there's nothing to check
|
||||||
if (beforeInfo === undefined) {
|
if (beforeInfo === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -163,6 +164,7 @@ export function validFillOrderAssertion(
|
|||||||
const expectedAggregatedStats = { ...beforeInfo.aggregatedStats };
|
const expectedAggregatedStats = { ...beforeInfo.aggregatedStats };
|
||||||
const expectedEvents = [];
|
const expectedEvents = [];
|
||||||
|
|
||||||
|
// Refer to `payProtocolFee`
|
||||||
if (beforeInfo.poolStake.isGreaterThanOrEqualTo(stakingConstants.DEFAULT_PARAMS.minimumPoolStake)) {
|
if (beforeInfo.poolStake.isGreaterThanOrEqualTo(stakingConstants.DEFAULT_PARAMS.minimumPoolStake)) {
|
||||||
if (beforeInfo.poolStats.feesCollected.isZero()) {
|
if (beforeInfo.poolStats.feesCollected.isZero()) {
|
||||||
const membersStakeInPool = beforeInfo.poolStake.minus(beforeInfo.operatorStake);
|
const membersStakeInPool = beforeInfo.poolStake.minus(beforeInfo.operatorStake);
|
||||||
@ -181,20 +183,23 @@ export function validFillOrderAssertion(
|
|||||||
expectedAggregatedStats.numPoolsToFinalize = beforeInfo.aggregatedStats.numPoolsToFinalize.plus(
|
expectedAggregatedStats.numPoolsToFinalize = beforeInfo.aggregatedStats.numPoolsToFinalize.plus(
|
||||||
1,
|
1,
|
||||||
);
|
);
|
||||||
|
// StakingPoolEarnedRewardsInEpoch event emitted
|
||||||
expectedEvents.push({
|
expectedEvents.push({
|
||||||
epoch: currentEpoch,
|
epoch: currentEpoch,
|
||||||
poolId: beforeInfo.poolId,
|
poolId: beforeInfo.poolId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// Credit a protocol fee to the maker's staking pool
|
||||||
expectedPoolStats.feesCollected = beforeInfo.poolStats.feesCollected.plus(
|
expectedPoolStats.feesCollected = beforeInfo.poolStats.feesCollected.plus(
|
||||||
DeploymentManager.protocolFee,
|
DeploymentManager.protocolFee,
|
||||||
);
|
);
|
||||||
|
// Update aggregated stats
|
||||||
expectedAggregatedStats.totalFeesCollected = beforeInfo.aggregatedStats.totalFeesCollected.plus(
|
expectedAggregatedStats.totalFeesCollected = beforeInfo.aggregatedStats.totalFeesCollected.plus(
|
||||||
DeploymentManager.protocolFee,
|
DeploymentManager.protocolFee,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for updated stats and event
|
||||||
const poolStats = PoolStats.fromArray(
|
const poolStats = PoolStats.fromArray(
|
||||||
await stakingWrapper.poolStatsByEpoch(beforeInfo.poolId, currentEpoch).callAsync(),
|
await stakingWrapper.poolStatsByEpoch(beforeInfo.poolId, currentEpoch).callAsync(),
|
||||||
);
|
);
|
||||||
|
@ -11,16 +11,17 @@ import { FunctionAssertion, FunctionResult } from './function_assertion';
|
|||||||
*/
|
*/
|
||||||
/* tslint:disable:no-unnecessary-type-assertion */
|
/* tslint:disable:no-unnecessary-type-assertion */
|
||||||
/* tslint:disable:no-non-null-assertion */
|
/* tslint:disable:no-non-null-assertion */
|
||||||
export function validJoinStakingPoolAssertion(deployment: DeploymentManager): FunctionAssertion<[string], {}, void> {
|
export function validJoinStakingPoolAssertion(deployment: DeploymentManager): FunctionAssertion<[string], void, void> {
|
||||||
const { stakingWrapper } = deployment.staking;
|
const { stakingWrapper } = deployment.staking;
|
||||||
|
|
||||||
return new FunctionAssertion<[string], {}, void>(stakingWrapper, 'joinStakingPoolAsMaker', {
|
return new FunctionAssertion<[string], void, void>(stakingWrapper, 'joinStakingPoolAsMaker', {
|
||||||
after: async (_beforeInfo, result: FunctionResult, args: [string], txData: Partial<TxData>) => {
|
after: async (_beforeInfo: void, result: FunctionResult, args: [string], txData: Partial<TxData>) => {
|
||||||
// Ensure that the tx succeeded.
|
// Ensure that the tx succeeded.
|
||||||
expect(result.success, `Error: ${result.data}`).to.be.true();
|
expect(result.success, `Error: ${result.data}`).to.be.true();
|
||||||
|
|
||||||
const [poolId] = args;
|
const [poolId] = args;
|
||||||
|
|
||||||
|
// Verify a MakerStakingPoolSet event was emitted
|
||||||
const logs = result.receipt!.logs;
|
const logs = result.receipt!.logs;
|
||||||
const logArgs = filterLogsToArguments<StakingMakerStakingPoolSetEventArgs>(
|
const logArgs = filterLogsToArguments<StakingMakerStakingPoolSetEventArgs>(
|
||||||
logs,
|
logs,
|
||||||
@ -32,6 +33,7 @@ export function validJoinStakingPoolAssertion(deployment: DeploymentManager): Fu
|
|||||||
poolId,
|
poolId,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
// Verify that the maker's pool id has been updated in storage
|
||||||
const joinedPoolId = await deployment.staking.stakingWrapper.poolIdByMaker(txData.from!).callAsync();
|
const joinedPoolId = await deployment.staking.stakingWrapper.poolIdByMaker(txData.from!).callAsync();
|
||||||
expect(joinedPoolId).to.be.eq(poolId);
|
expect(joinedPoolId).to.be.eq(poolId);
|
||||||
},
|
},
|
||||||
|
@ -75,8 +75,8 @@ function updateNextEpochBalances(
|
|||||||
return updatedPools;
|
return updatedPools;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Returns a FunctionAssertion for `moveStake` which assumes valid input is provided. The
|
* Returns a FunctionAssertion for `moveStake` which assumes valid input is provided. Checks that
|
||||||
* FunctionAssertion checks that the staker's
|
* the owner's stake and global stake by status get updated correctly.
|
||||||
*/
|
*/
|
||||||
/* tslint:disable:no-unnecessary-type-assertion */
|
/* tslint:disable:no-unnecessary-type-assertion */
|
||||||
export function validMoveStakeAssertion(
|
export function validMoveStakeAssertion(
|
||||||
|
@ -53,10 +53,7 @@ export function validStakeAssertion(
|
|||||||
await balanceStore.updateErc20BalancesAsync();
|
await balanceStore.updateErc20BalancesAsync();
|
||||||
balanceStore.assertEquals(expectedBalances);
|
balanceStore.assertEquals(expectedBalances);
|
||||||
|
|
||||||
// Checks that the owner's undelegated stake has increased by the stake amount
|
// _increaseCurrentAndNextBalance
|
||||||
const ownerUndelegatedStake = await stakingWrapper
|
|
||||||
.getOwnerStakeByStatus(txData.from as string, StakeStatus.Undelegated)
|
|
||||||
.callAsync();
|
|
||||||
loadCurrentBalance(ownerStake[StakeStatus.Undelegated], currentEpoch, true);
|
loadCurrentBalance(ownerStake[StakeStatus.Undelegated], currentEpoch, true);
|
||||||
ownerStake[StakeStatus.Undelegated].currentEpochBalance = ownerStake[
|
ownerStake[StakeStatus.Undelegated].currentEpochBalance = ownerStake[
|
||||||
StakeStatus.Undelegated
|
StakeStatus.Undelegated
|
||||||
@ -64,6 +61,11 @@ export function validStakeAssertion(
|
|||||||
ownerStake[StakeStatus.Undelegated].nextEpochBalance = ownerStake[
|
ownerStake[StakeStatus.Undelegated].nextEpochBalance = ownerStake[
|
||||||
StakeStatus.Undelegated
|
StakeStatus.Undelegated
|
||||||
].nextEpochBalance.plus(amount);
|
].nextEpochBalance.plus(amount);
|
||||||
|
|
||||||
|
// Checks that the owner's undelegated stake has increased by the stake amount
|
||||||
|
const ownerUndelegatedStake = await stakingWrapper
|
||||||
|
.getOwnerStakeByStatus(txData.from as string, StakeStatus.Undelegated)
|
||||||
|
.callAsync();
|
||||||
expect(ownerUndelegatedStake, 'Owner undelegated stake').to.deep.equal(ownerStake[StakeStatus.Undelegated]);
|
expect(ownerUndelegatedStake, 'Owner undelegated stake').to.deep.equal(ownerStake[StakeStatus.Undelegated]);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -53,10 +53,7 @@ export function validUnstakeAssertion(
|
|||||||
await balanceStore.updateErc20BalancesAsync();
|
await balanceStore.updateErc20BalancesAsync();
|
||||||
balanceStore.assertEquals(expectedBalances);
|
balanceStore.assertEquals(expectedBalances);
|
||||||
|
|
||||||
// Checks that the owner's undelegated stake has decreased by the stake amount
|
// _decreaseCurrentAndNextBalance
|
||||||
const ownerUndelegatedStake = await stakingWrapper
|
|
||||||
.getOwnerStakeByStatus(txData.from as string, StakeStatus.Undelegated)
|
|
||||||
.callAsync();
|
|
||||||
loadCurrentBalance(ownerStake[StakeStatus.Undelegated], currentEpoch, true);
|
loadCurrentBalance(ownerStake[StakeStatus.Undelegated], currentEpoch, true);
|
||||||
ownerStake[StakeStatus.Undelegated].currentEpochBalance = ownerStake[
|
ownerStake[StakeStatus.Undelegated].currentEpochBalance = ownerStake[
|
||||||
StakeStatus.Undelegated
|
StakeStatus.Undelegated
|
||||||
@ -64,6 +61,11 @@ export function validUnstakeAssertion(
|
|||||||
ownerStake[StakeStatus.Undelegated].nextEpochBalance = ownerStake[
|
ownerStake[StakeStatus.Undelegated].nextEpochBalance = ownerStake[
|
||||||
StakeStatus.Undelegated
|
StakeStatus.Undelegated
|
||||||
].nextEpochBalance.minus(amount);
|
].nextEpochBalance.minus(amount);
|
||||||
|
|
||||||
|
// Checks that the owner's undelegated stake has decreased by the stake amount
|
||||||
|
const ownerUndelegatedStake = await stakingWrapper
|
||||||
|
.getOwnerStakeByStatus(txData.from as string, StakeStatus.Undelegated)
|
||||||
|
.callAsync();
|
||||||
expect(ownerUndelegatedStake, 'Owner undelegated stake').to.deep.equal(ownerStake[StakeStatus.Undelegated]);
|
expect(ownerUndelegatedStake, 'Owner undelegated stake').to.deep.equal(ownerStake[StakeStatus.Undelegated]);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -16,9 +16,9 @@ interface WithdrawDelegatorRewardsBeforeInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a FunctionAssertion for `stake` which assumes valid input is provided. The
|
* Returns a FunctionAssertion for `withdrawDelegatorRewards` which assumes valid input is provided.
|
||||||
* FunctionAssertion checks that the staker and zrxVault's balances of ZRX decrease and increase,
|
* It checks that the delegator's stake gets synced and pool rewards are updated to reflect the
|
||||||
* respectively, by the input amount.
|
* amount withdrawn.
|
||||||
*/
|
*/
|
||||||
/* tslint:disable:no-unnecessary-type-assertion */
|
/* tslint:disable:no-unnecessary-type-assertion */
|
||||||
export function validWithdrawDelegatorRewardsAssertion(
|
export function validWithdrawDelegatorRewardsAssertion(
|
||||||
@ -50,12 +50,14 @@ export function validWithdrawDelegatorRewardsAssertion(
|
|||||||
const [poolId] = args;
|
const [poolId] = args;
|
||||||
const { currentEpoch } = simulationEnvironment;
|
const { currentEpoch } = simulationEnvironment;
|
||||||
|
|
||||||
|
// Check that delegator stake has been synced
|
||||||
const expectedDelegatorStake = loadCurrentBalance(beforeInfo.delegatorStake, currentEpoch);
|
const expectedDelegatorStake = loadCurrentBalance(beforeInfo.delegatorStake, currentEpoch);
|
||||||
const delegatorStake = await stakingWrapper
|
const delegatorStake = await stakingWrapper
|
||||||
.getStakeDelegatedToPoolByOwner(txData.from as string, poolId)
|
.getStakeDelegatedToPoolByOwner(txData.from as string, poolId)
|
||||||
.callAsync();
|
.callAsync();
|
||||||
expect(delegatorStake).to.deep.equal(expectedDelegatorStake);
|
expect(delegatorStake).to.deep.equal(expectedDelegatorStake);
|
||||||
|
|
||||||
|
// Check that pool rewards have been updated to reflect the amount withdrawn.
|
||||||
const transferEvents = filterLogsToArguments<WETH9TransferEventArgs>(
|
const transferEvents = filterLogsToArguments<WETH9TransferEventArgs>(
|
||||||
result.receipt!.logs, // tslint:disable-line:no-non-null-assertion
|
result.receipt!.logs, // tslint:disable-line:no-non-null-assertion
|
||||||
WETH9Events.Transfer,
|
WETH9Events.Transfer,
|
||||||
|
@ -58,12 +58,15 @@ blockchainTests('Staking rewards fuzz test', env => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('fuzz', async () => {
|
it('fuzz', async () => {
|
||||||
|
// Deploy contracts
|
||||||
const deployment = await DeploymentManager.deployAsync(env, {
|
const deployment = await DeploymentManager.deployAsync(env, {
|
||||||
numErc20TokensToDeploy: 4,
|
numErc20TokensToDeploy: 4,
|
||||||
numErc721TokensToDeploy: 0,
|
numErc721TokensToDeploy: 0,
|
||||||
numErc1155TokensToDeploy: 0,
|
numErc1155TokensToDeploy: 0,
|
||||||
});
|
});
|
||||||
const [ERC20TokenA, ERC20TokenB, ERC20TokenC, ERC20TokenD] = deployment.tokens.erc20;
|
const [ERC20TokenA, ERC20TokenB, ERC20TokenC, ERC20TokenD] = deployment.tokens.erc20;
|
||||||
|
|
||||||
|
// Set up balance store
|
||||||
const balanceStore = new BlockchainBalanceStore(
|
const balanceStore = new BlockchainBalanceStore(
|
||||||
{
|
{
|
||||||
StakingProxy: deployment.staking.stakingProxy.address,
|
StakingProxy: deployment.staking.stakingProxy.address,
|
||||||
@ -80,8 +83,11 @@ blockchainTests('Staking rewards fuzz test', env => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Set up simulation environment
|
||||||
const simulationEnvironment = new SimulationEnvironment(deployment, balanceStore);
|
const simulationEnvironment = new SimulationEnvironment(deployment, balanceStore);
|
||||||
|
|
||||||
|
// Spin up actors
|
||||||
const actors = [
|
const actors = [
|
||||||
new Maker({ deployment, simulationEnvironment, name: 'Maker 1' }),
|
new Maker({ deployment, simulationEnvironment, name: 'Maker 1' }),
|
||||||
new Maker({ deployment, simulationEnvironment, name: 'Maker 2' }),
|
new Maker({ deployment, simulationEnvironment, name: 'Maker 2' }),
|
||||||
@ -98,14 +104,17 @@ blockchainTests('Staking rewards fuzz test', env => {
|
|||||||
new OperatorStakerMaker({ deployment, simulationEnvironment, name: 'Operator/Staker/Maker' }),
|
new OperatorStakerMaker({ deployment, simulationEnvironment, name: 'Operator/Staker/Maker' }),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// 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);
|
const takers = filterActorsByRole(actors, Taker);
|
||||||
for (const taker of takers) {
|
for (const taker of takers) {
|
||||||
await taker.configureERC20TokenAsync(deployment.tokens.weth, deployment.staking.stakingProxy.address);
|
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);
|
const stakers = filterActorsByRole(actors, Staker);
|
||||||
for (const staker of stakers) {
|
for (const staker of stakers) {
|
||||||
await staker.configureERC20TokenAsync(deployment.tokens.zrx);
|
await staker.configureERC20TokenAsync(deployment.tokens.zrx);
|
||||||
}
|
}
|
||||||
|
// Register each actor in the balance store
|
||||||
for (const actor of actors) {
|
for (const actor of actors) {
|
||||||
balanceStore.registerTokenOwner(actor.address, actor.name);
|
balanceStore.registerTokenOwner(actor.address, actor.name);
|
||||||
}
|
}
|
||||||
|
@ -58,6 +58,7 @@ contract TestStaking is
|
|||||||
testZrxVaultAddress = zrxVaultAddress;
|
testZrxVaultAddress = zrxVaultAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @dev Gets the most recent cumulative reward for a pool, and the epoch it was stored.
|
||||||
function getMostRecentCumulativeReward(bytes32 poolId)
|
function getMostRecentCumulativeReward(bytes32 poolId)
|
||||||
external
|
external
|
||||||
view
|
view
|
||||||
|
@ -67,6 +67,10 @@ export class StoredBalance {
|
|||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simulates _loadCurrentBalance. `shouldMutate` flag specifies whether or not to update the given
|
||||||
|
* StoredBalance instance.
|
||||||
|
*/
|
||||||
export function loadCurrentBalance(
|
export function loadCurrentBalance(
|
||||||
balance: StoredBalance,
|
balance: StoredBalance,
|
||||||
epoch: BigNumber,
|
epoch: BigNumber,
|
||||||
@ -85,11 +89,17 @@ export function loadCurrentBalance(
|
|||||||
return loadedBalance;
|
return loadedBalance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simulates _incrementNextEpochBalance
|
||||||
|
*/
|
||||||
export function incrementNextEpochBalance(balance: StoredBalance, amount: Numberish, epoch: BigNumber): void {
|
export function incrementNextEpochBalance(balance: StoredBalance, amount: Numberish, epoch: BigNumber): void {
|
||||||
loadCurrentBalance(balance, epoch, true);
|
loadCurrentBalance(balance, epoch, true);
|
||||||
balance.nextEpochBalance = balance.nextEpochBalance.plus(amount);
|
balance.nextEpochBalance = balance.nextEpochBalance.plus(amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simulates _decrementNextEpochBalance
|
||||||
|
*/
|
||||||
export function decrementNextEpochBalance(balance: StoredBalance, amount: Numberish, epoch: BigNumber): void {
|
export function decrementNextEpochBalance(balance: StoredBalance, amount: Numberish, epoch: BigNumber): void {
|
||||||
loadCurrentBalance(balance, epoch, true);
|
loadCurrentBalance(balance, epoch, true);
|
||||||
balance.nextEpochBalance = balance.nextEpochBalance.minus(amount);
|
balance.nextEpochBalance = balance.nextEpochBalance.minus(amount);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user