Add optional distribution parameter to Pseudorandom.integer, use Kumaraswamy distribution for operator share
This commit is contained in:
parent
7cbffdb86b
commit
1bd906ecb3
@ -111,7 +111,7 @@ export function MakerMixin<TBase extends Constructor>(Base: TBase): TBase & Cons
|
||||
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));
|
||||
return Pseudorandom.integer(0, balance.dividedToIntegerBy(2));
|
||||
}),
|
||||
);
|
||||
// Encode asset data
|
||||
|
@ -6,7 +6,7 @@ import * as _ from 'lodash';
|
||||
import { validCreateStakingPoolAssertion } from '../assertions/createStakingPool';
|
||||
import { validDecreaseStakingPoolOperatorShareAssertion } from '../assertions/decreaseStakingPoolOperatorShare';
|
||||
import { AssertionResult } from '../assertions/function_assertion';
|
||||
import { Pseudorandom } from '../utils/pseudorandom';
|
||||
import { Distributions, Pseudorandom } from '../utils/pseudorandom';
|
||||
|
||||
import { Actor, Constructor } from './base';
|
||||
|
||||
@ -83,7 +83,11 @@ export function PoolOperatorMixin<TBase extends Constructor>(Base: TBase): TBase
|
||||
private async *_validCreateStakingPool(): AsyncIterableIterator<AssertionResult> {
|
||||
const assertion = validCreateStakingPoolAssertion(this.actor.deployment, this.actor.simulationEnvironment!);
|
||||
while (true) {
|
||||
const operatorShare = Pseudorandom.integer(constants.PPM).toNumber();
|
||||
const operatorShare = Pseudorandom.integer(
|
||||
0,
|
||||
constants.PPM,
|
||||
Distributions.Kumaraswamy(0.2, 0.2),
|
||||
).toNumber();
|
||||
yield assertion.executeAsync([operatorShare, false], { from: this.actor.address });
|
||||
}
|
||||
}
|
||||
@ -96,7 +100,11 @@ export function PoolOperatorMixin<TBase extends Constructor>(Base: TBase): TBase
|
||||
if (poolId === undefined) {
|
||||
yield undefined;
|
||||
} else {
|
||||
const operatorShare = Pseudorandom.integer(stakingPools[poolId].operatorShare).toNumber();
|
||||
const operatorShare = Pseudorandom.integer(
|
||||
0,
|
||||
stakingPools[poolId].operatorShare,
|
||||
Distributions.Kumaraswamy(0.2, 0.2),
|
||||
).toNumber();
|
||||
yield assertion.executeAsync([poolId, operatorShare], { from: this.actor.address });
|
||||
}
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ export function StakerMixin<TBase extends Constructor>(Base: TBase): TBase & Con
|
||||
while (true) {
|
||||
await balanceStore.updateErc20BalancesAsync();
|
||||
const zrxBalance = balanceStore.balances.erc20[this.actor.address][zrx.address];
|
||||
const amount = Pseudorandom.integer(zrxBalance);
|
||||
const amount = Pseudorandom.integer(0, zrxBalance);
|
||||
yield assertion.executeAsync([amount], { from: this.actor.address });
|
||||
}
|
||||
}
|
||||
@ -98,7 +98,7 @@ export function StakerMixin<TBase extends Constructor>(Base: TBase): TBase & Con
|
||||
undelegatedStake.currentEpochBalance,
|
||||
undelegatedStake.nextEpochBalance,
|
||||
);
|
||||
const amount = Pseudorandom.integer(withdrawableStake);
|
||||
const amount = Pseudorandom.integer(0, withdrawableStake);
|
||||
yield assertion.executeAsync([amount], { from: this.actor.address });
|
||||
}
|
||||
}
|
||||
@ -136,7 +136,7 @@ export function StakerMixin<TBase extends Constructor>(Base: TBase): TBase & Con
|
||||
from.status === StakeStatus.Undelegated
|
||||
? this.stake[StakeStatus.Undelegated].nextEpochBalance
|
||||
: this.stake[StakeStatus.Delegated][from.poolId].nextEpochBalance;
|
||||
const amount = Pseudorandom.integer(moveableStake);
|
||||
const amount = Pseudorandom.integer(0, moveableStake);
|
||||
|
||||
yield assertion.executeAsync([from, to, amount], { from: this.actor.address });
|
||||
}
|
||||
|
@ -75,12 +75,12 @@ export function TakerMixin<TBase extends Constructor>(Base: TBase): TBase & Cons
|
||||
// Maker creates and signs a fillable order
|
||||
const order = await maker.createFillableOrderAsync(this.actor);
|
||||
// Taker fills the order by a random amount (up to the order's takerAssetAmount)
|
||||
const fillAmount = Pseudorandom.integer(order.takerAssetAmount);
|
||||
const fillAmount = Pseudorandom.integer(0, 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], {
|
||||
from: this.actor.address,
|
||||
value: Pseudorandom.integer(DeploymentManager.protocolFee.times(2)),
|
||||
value: Pseudorandom.integer(0, DeploymentManager.protocolFee.times(2)),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -13,11 +13,11 @@ import { FunctionAssertion, FunctionResult } from './function_assertion';
|
||||
export function validDecreaseStakingPoolOperatorShareAssertion(
|
||||
deployment: DeploymentManager,
|
||||
pools: StakingPoolById,
|
||||
): FunctionAssertion<[string, number], {}, void> {
|
||||
): FunctionAssertion<[string, number], void, void> {
|
||||
const { stakingWrapper } = deployment.staking;
|
||||
|
||||
return new FunctionAssertion<[string, number], {}, void>(stakingWrapper, 'decreaseStakingPoolOperatorShare', {
|
||||
after: async (_beforeInfo, result: FunctionResult, args: [string, number], _txData: Partial<TxData>) => {
|
||||
return new FunctionAssertion<[string, number], void, void>(stakingWrapper, 'decreaseStakingPoolOperatorShare', {
|
||||
after: async (_beforeInfo: void, result: FunctionResult, args: [string, number], _txData: Partial<TxData>) => {
|
||||
// Ensure that the tx succeeded.
|
||||
expect(result.success, `Error: ${result.data}`).to.be.true();
|
||||
|
||||
|
@ -4,7 +4,7 @@ import * as seedrandom from 'seedrandom';
|
||||
|
||||
class PRNGWrapper {
|
||||
public readonly seed = process.env.SEED || Math.random().toString();
|
||||
private readonly _rng = seedrandom(this.seed);
|
||||
public readonly rng = seedrandom(this.seed);
|
||||
|
||||
/*
|
||||
* Pseudorandom version of _.sample. Picks an element of the given array with uniform probability.
|
||||
@ -14,7 +14,7 @@ class PRNGWrapper {
|
||||
if (arr.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
const index = Math.abs(this._rng.int32()) % arr.length;
|
||||
const index = Math.abs(this.rng.int32()) % arr.length;
|
||||
return arr[index];
|
||||
}
|
||||
|
||||
@ -33,22 +33,39 @@ class PRNGWrapper {
|
||||
return samples;
|
||||
}
|
||||
|
||||
// tslint:disable:unified-signatures
|
||||
/*
|
||||
* Pseudorandom version of getRandomPortion/getRandomInteger. If two arguments are provided,
|
||||
* treats those arguments as the min and max (inclusive) of the desired range. If only one
|
||||
* argument is provided, picks an integer between 0 and the argument.
|
||||
* Pseudorandom version of getRandomPortion/getRandomInteger. If no distribution is provided,
|
||||
* samples an integer between the min and max uniformly at random. If a distribution is
|
||||
* provided, samples an integer from the given distribution (assumed to be defined on the
|
||||
* interval [0, 1]) scaled to [min, max].
|
||||
*/
|
||||
public integer(max: Numberish): BigNumber;
|
||||
public integer(min: Numberish, max: Numberish): BigNumber;
|
||||
public integer(a: Numberish, b?: Numberish): BigNumber {
|
||||
if (b === undefined) {
|
||||
return new BigNumber(this._rng()).times(a).integerValue(BigNumber.ROUND_HALF_UP);
|
||||
} else {
|
||||
const range = new BigNumber(b).minus(a);
|
||||
return this.integer(range).plus(a);
|
||||
public integer(min: Numberish, max: Numberish, distribution: () => Numberish = this.rng): BigNumber {
|
||||
const range = new BigNumber(max).minus(min);
|
||||
return new BigNumber(distribution())
|
||||
.times(range)
|
||||
.integerValue(BigNumber.ROUND_HALF_UP)
|
||||
.plus(min);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a function that produces samples from the Kumaraswamy distribution parameterized by
|
||||
* the given alpha and beta. The Kumaraswamy distribution is like the beta distribution, but
|
||||
* with a nice closed form.
|
||||
* https://en.wikipedia.org/wiki/Kumaraswamy_distribution
|
||||
* https://www.johndcook.com/blog/2009/11/24/kumaraswamy-distribution/
|
||||
*/
|
||||
public kumaraswamy(this: PRNGWrapper, alpha: Numberish, beta: Numberish): () => BigNumber {
|
||||
const ONE = new BigNumber(1);
|
||||
return () => {
|
||||
const u = new BigNumber(this.rng()).modulo(ONE); // u ~ Uniform(0, 1)
|
||||
// Evaluate the inverse CDF at `u` to obtain a sample from Kumaraswamy(alpha, beta)
|
||||
return ONE.minus(ONE.minus(u).exponentiatedBy(ONE.dividedBy(beta))).exponentiatedBy(ONE.dividedBy(alpha));
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const Pseudorandom = new PRNGWrapper();
|
||||
export const Distributions = {
|
||||
Uniform: Pseudorandom.rng,
|
||||
Kumaraswamy: Pseudorandom.kumaraswamy.bind(Pseudorandom),
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user