diff --git a/contracts/exchange-libs/src/reference_functions.ts b/contracts/exchange-libs/src/reference_functions.ts index fa57f09b70..d1b70fd397 100644 --- a/contracts/exchange-libs/src/reference_functions.ts +++ b/contracts/exchange-libs/src/reference_functions.ts @@ -103,7 +103,7 @@ export function calculateFillResults( order.takerAssetAmount, order.makerAssetAmount, ); - const makerFeePaid = safeGetPartialAmountFloor(makerAssetFilledAmount, order.makerAssetAmount, order.makerFee); + const makerFeePaid = safeGetPartialAmountFloor(takerAssetFilledAmount, order.takerAssetAmount, order.makerFee); const takerFeePaid = safeGetPartialAmountFloor(takerAssetFilledAmount, order.takerAssetAmount, order.takerFee); return { makerAssetFilledAmount, diff --git a/contracts/integrations/test/framework/actors/base.ts b/contracts/integrations/test/framework/actors/base.ts index c73d15ec1c..610337f1f9 100644 --- a/contracts/integrations/test/framework/actors/base.ts +++ b/contracts/integrations/test/framework/actors/base.ts @@ -47,7 +47,10 @@ export class Actor { this.name = config.name || this.address; this.deployment = config.deployment; this.privateKey = constants.TESTRPC_PRIVATE_KEYS[config.deployment.accounts.indexOf(this.address)]; - this.simulationEnvironment = config.simulationEnvironment; + if (config.simulationEnvironment !== undefined) { + this.simulationEnvironment = config.simulationEnvironment; + this.simulationEnvironment.actors.push(this); + } this._transactionFactory = new TransactionFactory( this.privateKey, config.deployment.exchange.address, @@ -123,7 +126,6 @@ export class Actor { if (logs.length !== 1) { throw new Error('Invalid number of `TransferSingle` logs'); } - const { id } = logs[0]; // Mint the token diff --git a/contracts/integrations/test/framework/actors/fee_recipient.ts b/contracts/integrations/test/framework/actors/fee_recipient.ts index 4cdace00f3..bb60028cf0 100644 --- a/contracts/integrations/test/framework/actors/fee_recipient.ts +++ b/contracts/integrations/test/framework/actors/fee_recipient.ts @@ -18,7 +18,7 @@ export interface FeeRecipientInterface { } /** - * This mixin encapsulates functionaltiy associated with fee recipients within the 0x ecosystem. + * This mixin encapsulates functionality associated with fee recipients within the 0x ecosystem. * As of writing, the only extra functionality provided is signing Coordinator approvals. */ export function FeeRecipientMixin(Base: TBase): TBase & Constructor { diff --git a/contracts/integrations/test/framework/actors/keeper.ts b/contracts/integrations/test/framework/actors/keeper.ts index b16182b749..f26a1a1203 100644 --- a/contracts/integrations/test/framework/actors/keeper.ts +++ b/contracts/integrations/test/framework/actors/keeper.ts @@ -1,4 +1,8 @@ -import { IStakingEventsStakingPoolEarnedRewardsInEpochEventArgs, TestStakingEvents } from '@0x/contracts-staking'; +import { + IStakingEventsStakingPoolEarnedRewardsInEpochEventArgs, + TestStakingContract, + TestStakingEvents, +} from '@0x/contracts-staking'; import { filterLogsToArguments, web3Wrapper } from '@0x/contracts-test-utils'; import { BigNumber } from '@0x/utils'; import { BlockParamLiteral, TransactionReceiptWithDecodedLogs } from 'ethereum-types'; @@ -10,8 +14,19 @@ export interface KeeperInterface { finalizePoolsAsync: (poolIds?: string[]) => Promise; } +async function fastForwardToNextEpochAsync(stakingContract: TestStakingContract): Promise { + // increase timestamp of next block by how many seconds we need to + // get to the next epoch. + const epochEndTime = await stakingContract.getCurrentEpochEarliestEndTimeInSeconds().callAsync(); + const lastBlockTime = await web3Wrapper.getBlockTimestampAsync('latest'); + const dt = Math.max(0, epochEndTime.minus(lastBlockTime).toNumber()); + await web3Wrapper.increaseTimeAsync(dt); + // mine next block + await web3Wrapper.mineBlockAsync(); +} + /** - * This mixin encapsulates functionaltiy associated with keepers within the 0x ecosystem. + * This mixin encapsulates functionality associated with keepers within the 0x ecosystem. * This includes ending epochs sand finalizing pools in the staking system. */ export function KeeperMixin(Base: TBase): TBase & Constructor { @@ -35,14 +50,7 @@ export function KeeperMixin(Base: TBase): TBase & Con public async endEpochAsync(shouldFastForward: boolean = true): Promise { const { stakingWrapper } = this.actor.deployment.staking; if (shouldFastForward) { - // increase timestamp of next block by how many seconds we need to - // get to the next epoch. - const epochEndTime = await stakingWrapper.getCurrentEpochEarliestEndTimeInSeconds().callAsync(); - const lastBlockTime = await web3Wrapper.getBlockTimestampAsync('latest'); - const dt = Math.max(0, epochEndTime.minus(lastBlockTime).toNumber()); - await web3Wrapper.increaseTimeAsync(dt); - // mine next block - await web3Wrapper.mineBlockAsync(); + await fastForwardToNextEpochAsync(stakingWrapper); } return stakingWrapper.endEpoch().awaitTransactionSuccessAsync({ from: this.actor.address }); } diff --git a/contracts/integrations/test/framework/actors/maker.ts b/contracts/integrations/test/framework/actors/maker.ts index a6e9452a69..b724739305 100644 --- a/contracts/integrations/test/framework/actors/maker.ts +++ b/contracts/integrations/test/framework/actors/maker.ts @@ -21,7 +21,7 @@ export interface MakerInterface { } /** - * This mixin encapsulates functionaltiy associated with makers within the 0x ecosystem. + * This mixin encapsulates functionality associated with makers within the 0x ecosystem. * This includes signing and canceling orders, as well as joining a staking pool as a maker. */ export function MakerMixin(Base: TBase): TBase & Constructor { @@ -90,7 +90,7 @@ export function MakerMixin(Base: TBase): TBase & Cons while (true) { const poolId = Pseudorandom.sample(Object.keys(stakingPools)); if (poolId === undefined) { - yield undefined; + yield; } else { yield assertion.executeAsync([poolId], { from: this.actor.address }); } diff --git a/contracts/integrations/test/framework/actors/pool_operator.ts b/contracts/integrations/test/framework/actors/pool_operator.ts index 494f082838..d9b973a006 100644 --- a/contracts/integrations/test/framework/actors/pool_operator.ts +++ b/contracts/integrations/test/framework/actors/pool_operator.ts @@ -19,7 +19,7 @@ export interface PoolOperatorInterface { } /** - * This mixin encapsulates functionaltiy associated with pool operators within the 0x ecosystem. + * This mixin encapsulates functionality associated with pool operators within the 0x ecosystem. * This includes creating staking pools and decreasing the operator share of a pool. */ export function PoolOperatorMixin(Base: TBase): TBase & Constructor { diff --git a/contracts/integrations/test/framework/actors/staker.ts b/contracts/integrations/test/framework/actors/staker.ts index c75be0c36d..10c06f0def 100644 --- a/contracts/integrations/test/framework/actors/staker.ts +++ b/contracts/integrations/test/framework/actors/staker.ts @@ -16,7 +16,7 @@ export interface StakerInterface { } /** - * This mixin encapsulates functionaltiy associated with stakers within the 0x ecosystem. + * This mixin encapsulates functionality associated with stakers within the 0x ecosystem. * This includes staking ZRX (and optionally delegating it to a specific pool). */ export function StakerMixin(Base: TBase): TBase & Constructor { diff --git a/contracts/integrations/test/framework/actors/taker.ts b/contracts/integrations/test/framework/actors/taker.ts index 7a617e2dad..9516582cec 100644 --- a/contracts/integrations/test/framework/actors/taker.ts +++ b/contracts/integrations/test/framework/actors/taker.ts @@ -1,14 +1,17 @@ +import { DummyERC20TokenContract } from '@0x/contracts-erc20'; import { constants } from '@0x/contracts-test-utils'; import { SignedOrder } from '@0x/types'; import { BigNumber } from '@0x/utils'; import { TransactionReceiptWithDecodedLogs, TxData } from 'ethereum-types'; -import { validFillOrderCompleteFillAssertion } from '../assertions/fillOrder'; +import { validFillOrderAssertion } from '../assertions/fillOrder'; import { AssertionResult } from '../assertions/function_assertion'; import { DeploymentManager } from '../deployment_manager'; import { Pseudorandom } from '../utils/pseudorandom'; import { Actor, Constructor } from './base'; +import { Maker } from './maker'; +import { filterActorsByRole } from './utils'; export interface TakerInterface { fillOrderAsync: ( @@ -19,7 +22,7 @@ export interface TakerInterface { } /** - * This mixin encapsulates functionaltiy associated with takers within the 0x ecosystem. + * This mixin encapsulates functionality associated with takers within the 0x ecosystem. * As of writing, the only extra functionality provided is a utility wrapper around `fillOrder`, */ export function TakerMixin(Base: TBase): TBase & Constructor { @@ -39,7 +42,7 @@ export function TakerMixin(Base: TBase): TBase & Cons // Register this mixin's assertion generators this.actor.simulationActions = { ...this.actor.simulationActions, - validFillOrderCompleteFill: this._validFillOrderCompleteFill(), + validFillOrder: this._validFillOrder(), }; } @@ -61,31 +64,66 @@ export function TakerMixin(Base: TBase): TBase & Cons }); } - private async *_validFillOrderCompleteFill(): AsyncIterableIterator { - const { marketMakers } = this.actor.simulationEnvironment!; - const assertion = validFillOrderCompleteFillAssertion(this.actor.deployment); + private async *_validFillOrder(): AsyncIterableIterator { + const { actors, balanceStore } = this.actor.simulationEnvironment!; + const assertion = validFillOrderAssertion(this.actor.deployment); while (true) { - const maker = Pseudorandom.sample(marketMakers); + const maker = Pseudorandom.sample(filterActorsByRole(actors, Maker)); if (maker === undefined) { - yield undefined; + yield; } else { - // Configure the maker's token balances so that the order will definitely be fillable. - await Promise.all([ - ...this.actor.deployment.tokens.erc20.map(async token => maker.configureERC20TokenAsync(token)), - ...this.actor.deployment.tokens.erc20.map(async token => - this.actor.configureERC20TokenAsync(token), + await balanceStore.updateErc20BalancesAsync(); + const [makerToken, makerFeeToken, takerToken, takerFeeToken] = Pseudorandom.sampleSize( + this.actor.deployment.tokens.erc20, + 4, // tslint:disable-line:custom-no-magic-numbers + ); + + const configureOrderAssetAsync = async ( + owner: Actor, + token: DummyERC20TokenContract, + ): Promise => { + let balance = balanceStore.balances.erc20[owner.address][token.address]; + if (balance === undefined || balance.isZero()) { + 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( + [ + [maker, makerToken], + [maker, makerFeeToken], + [this.actor, takerToken], + [this.actor, takerFeeToken], + ].map(async ([owner, token]) => + configureOrderAssetAsync(owner as Actor, token as DummyERC20TokenContract), ), - this.actor.configureERC20TokenAsync( - this.actor.deployment.tokens.weth, - this.actor.deployment.staking.stakingProxy.address, - ), - ]); + ); + const [makerAssetData, makerFeeAssetData, takerAssetData, takerFeeAssetData] = [ + makerToken, + makerFeeToken, + takerToken, + takerFeeToken, + ].map(token => + this.actor.deployment.assetDataEncoder.ERC20Token(token.address).getABIEncodedTransactionData(), + ); const order = await maker.signOrderAsync({ - makerAssetAmount: Pseudorandom.integer(constants.INITIAL_ERC20_BALANCE), - takerAssetAmount: Pseudorandom.integer(constants.INITIAL_ERC20_BALANCE), + makerAssetData, + takerAssetData, + makerFeeAssetData, + takerFeeAssetData, + makerAssetAmount, + takerAssetAmount, + makerFee, + takerFee, + feeRecipientAddress: Pseudorandom.sample(actors)!.address, }); - yield assertion.executeAsync([order, order.takerAssetAmount, order.signature], { + + const fillAmount = Pseudorandom.integer(order.takerAssetAmount); + yield assertion.executeAsync([order, fillAmount, order.signature], { from: this.actor.address, }); } diff --git a/contracts/integrations/test/framework/actors/utils.ts b/contracts/integrations/test/framework/actors/utils.ts index 2164cbeec6..6206d842a1 100644 --- a/contracts/integrations/test/framework/actors/utils.ts +++ b/contracts/integrations/test/framework/actors/utils.ts @@ -1,7 +1,7 @@ import { ObjectMap } from '@0x/types'; import * as _ from 'lodash'; -import { Actor } from './base'; +import { Actor, Constructor } from './base'; /** * Utility function to convert Actors into an object mapping readable names to addresses. @@ -10,3 +10,13 @@ import { Actor } from './base'; export function actorAddressesByName(actors: Actor[]): ObjectMap { return _.zipObject(actors.map(actor => actor.name), actors.map(actor => actor.address)); } + +/** + * Filters the given actors by class. + */ +export function filterActorsByRole( + actors: Actor[], + role: TClass, +): Array> { + return actors.filter(actor => actor instanceof role) as InstanceType; +} diff --git a/contracts/integrations/test/framework/assertions/createStakingPool.ts b/contracts/integrations/test/framework/assertions/createStakingPool.ts index c3ad30fe8d..1e488499b1 100644 --- a/contracts/integrations/test/framework/assertions/createStakingPool.ts +++ b/contracts/integrations/test/framework/assertions/createStakingPool.ts @@ -1,4 +1,4 @@ -import { StakingPoolById, StoredBalance } from '@0x/contracts-staking'; +import { StakingPool, StakingPoolById } from '@0x/contracts-staking'; import { expect } from '@0x/contracts-test-utils'; import { BigNumber } from '@0x/utils'; import { TxData } from 'ethereum-types'; @@ -44,11 +44,7 @@ export function validCreateStakingPoolAssertion( expect(actualPoolId).to.equal(expectedPoolId); // Adds the new pool to local state - pools[actualPoolId] = { - operator: txData.from!, - operatorShare, - delegatedStake: new StoredBalance(), - }; + pools[actualPoolId] = new StakingPool(txData.from!, operatorShare); }, }); } diff --git a/contracts/integrations/test/framework/assertions/fillOrder.ts b/contracts/integrations/test/framework/assertions/fillOrder.ts index fd8dcc7fac..3a4f9a14fa 100644 --- a/contracts/integrations/test/framework/assertions/fillOrder.ts +++ b/contracts/integrations/test/framework/assertions/fillOrder.ts @@ -1,6 +1,7 @@ import { ERC20TokenEvents, ERC20TokenTransferEventArgs } from '@0x/contracts-erc20'; import { ExchangeEvents, ExchangeFillEventArgs } from '@0x/contracts-exchange'; -import { constants, expect, orderHashUtils, verifyEvents } from '@0x/contracts-test-utils'; +import { ReferenceFunctions } from '@0x/contracts-exchange-libs'; +import { expect, orderHashUtils, verifyEvents } from '@0x/contracts-test-utils'; import { FillResults, Order } from '@0x/types'; import { BigNumber } from '@0x/utils'; import { TransactionReceiptWithDecodedLogs, TxData } from 'ethereum-types'; @@ -15,7 +16,14 @@ function verifyFillEvents( order: Order, receipt: TransactionReceiptWithDecodedLogs, deployment: DeploymentManager, + takerAssetFillAmount: BigNumber, ): void { + const fillResults = ReferenceFunctions.calculateFillResults( + order, + takerAssetFillAmount, + DeploymentManager.protocolFeeMultiplier, + DeploymentManager.gasPrice, + ); // Ensure that the fill event was correct. verifyEvents( receipt, @@ -30,11 +38,7 @@ function verifyFillEvents( orderHash: orderHashUtils.getOrderHashHex(order), takerAddress, senderAddress: takerAddress, - makerAssetFilledAmount: order.makerAssetAmount, - takerAssetFilledAmount: order.takerAssetAmount, - makerFeePaid: constants.ZERO_AMOUNT, - takerFeePaid: constants.ZERO_AMOUNT, - protocolFeePaid: DeploymentManager.protocolFee, + ...fillResults, }, ], ExchangeEvents.Fill, @@ -47,12 +51,22 @@ function verifyFillEvents( { _from: takerAddress, _to: order.makerAddress, - _value: order.takerAssetAmount, + _value: fillResults.takerAssetFilledAmount, }, { _from: order.makerAddress, _to: takerAddress, - _value: order.makerAssetAmount, + _value: fillResults.makerAssetFilledAmount, + }, + { + _from: takerAddress, + _to: order.feeRecipientAddress, + _value: fillResults.takerFeePaid, + }, + { + _from: order.makerAddress, + _to: order.feeRecipientAddress, + _value: fillResults.makerFeePaid, }, { _from: takerAddress, @@ -69,7 +83,7 @@ function verifyFillEvents( */ /* tslint:disable:no-unnecessary-type-assertion */ /* tslint:disable:no-non-null-assertion */ -export function validFillOrderCompleteFillAssertion( +export function validFillOrderAssertion( deployment: DeploymentManager, ): FunctionAssertion<[Order, BigNumber, string], {}, FillResults> { const exchange = deployment.exchange; @@ -81,13 +95,13 @@ export function validFillOrderCompleteFillAssertion( args: [Order, BigNumber, string], txData: Partial, ) => { - const [order] = args; + const [order, fillAmount] = args; // Ensure that the tx succeeded. - expect(result.success).to.be.true(); + expect(result.success, `Error: ${result.data}`).to.be.true(); // Ensure that the correct events were emitted. - verifyFillEvents(txData.from!, order, result.receipt!, deployment); + verifyFillEvents(txData.from!, order, result.receipt!, deployment, fillAmount); // TODO: Add validation for on-chain state (like balances) }, diff --git a/contracts/integrations/test/framework/simulation.ts b/contracts/integrations/test/framework/simulation.ts index 3bbd778ba1..c618239b4e 100644 --- a/contracts/integrations/test/framework/simulation.ts +++ b/contracts/integrations/test/framework/simulation.ts @@ -1,6 +1,13 @@ -import { GlobalStakeByStatus, StakeStatus, StakingPoolById, StoredBalance } from '@0x/contracts-staking'; +import { + constants as stakingConstants, + GlobalStakeByStatus, + StakeStatus, + StakingPoolById, + StoredBalance, +} from '@0x/contracts-staking'; +import { BigNumber } from '@0x/utils'; -import { Maker } from './actors/maker'; +import { Actor } from './actors/base'; import { AssertionResult } from './assertions/function_assertion'; import { BlockchainBalanceStore } from './balances/blockchain_balance_store'; import { DeploymentManager } from './deployment_manager'; @@ -14,11 +21,12 @@ export class SimulationEnvironment { [StakeStatus.Delegated]: new StoredBalance(), }; public stakingPools: StakingPoolById = {}; + public currentEpoch: BigNumber = stakingConstants.INITIAL_EPOCH; public constructor( public readonly deployment: DeploymentManager, public balanceStore: BlockchainBalanceStore, - public marketMakers: Maker[] = [], + public actors: Actor[] = [], ) {} public state(): any { diff --git a/contracts/integrations/test/framework/utils/pseudorandom.ts b/contracts/integrations/test/framework/utils/pseudorandom.ts index 874338a5b0..93f4e52e57 100644 --- a/contracts/integrations/test/framework/utils/pseudorandom.ts +++ b/contracts/integrations/test/framework/utils/pseudorandom.ts @@ -18,6 +18,21 @@ class PRNGWrapper { return arr[index]; } + /* + * Pseudorandom version of _.sampleSize. Returns an array of `n` samples from the given array + * (with replacement), chosen with uniform probability. Return undefined if the array is empty. + */ + public sampleSize(arr: T[], n: number): T[] | undefined { + if (arr.length === 0) { + return undefined; + } + const samples = []; + for (let i = 0; i < n; i++) { + samples.push(this.sample(arr) as T); + } + return samples; + } + // tslint:disable:unified-signatures /* * Pseudorandom version of getRandomPortion/getRandomInteger. If two arguments are provided, diff --git a/contracts/integrations/test/fuzz_tests/pool_membership_test.ts b/contracts/integrations/test/fuzz_tests/pool_membership_test.ts index d17382c8aa..bdf25b9600 100644 --- a/contracts/integrations/test/fuzz_tests/pool_membership_test.ts +++ b/contracts/integrations/test/fuzz_tests/pool_membership_test.ts @@ -1,7 +1,10 @@ -import { blockchainTests, constants } from '@0x/contracts-test-utils'; +import { blockchainTests } from '@0x/contracts-test-utils'; +import { Actor } from '../framework/actors/base'; import { MakerTaker } from '../framework/actors/hybrids'; import { Maker } from '../framework/actors/maker'; +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'; @@ -12,19 +15,15 @@ import { PoolManagementSimulation } from './pool_management_test'; class PoolMembershipSimulation extends Simulation { protected async *_assertionGenerator(): AsyncIterableIterator { - const { deployment } = this.environment; + const { actors } = this.environment; + const makers = filterActorsByRole(actors, Maker); + const takers = filterActorsByRole(actors, Taker); const poolManagement = new PoolManagementSimulation(this.environment); - const member = new MakerTaker({ - name: 'member', - deployment, - simulationEnvironment: this.environment, - }); - const actions = [ - member.simulationActions.validJoinStakingPool, - member.simulationActions.validFillOrderCompleteFill, + ...makers.map(maker => maker.simulationActions.validJoinStakingPool), + ...takers.map(taker => taker.simulationActions.validFillOrder), poolManagement.generator, ]; @@ -36,45 +35,23 @@ class PoolMembershipSimulation extends Simulation { } blockchainTests('pool membership fuzz test', env => { - let deployment: DeploymentManager; - let maker: Maker; - before(async function(): Promise { if (process.env.FUZZ_TEST !== 'pool_membership') { this.skip(); } + }); - deployment = await DeploymentManager.deployAsync(env, { - numErc20TokensToDeploy: 2, + after(async () => { + Actor.count = 0; + }); + + it('fuzz', async () => { + const deployment = await DeploymentManager.deployAsync(env, { + numErc20TokensToDeploy: 4, numErc721TokensToDeploy: 0, numErc1155TokensToDeploy: 0, }); - const makerToken = deployment.tokens.erc20[0]; - const takerToken = deployment.tokens.erc20[1]; - - const orderConfig = { - feeRecipientAddress: constants.NULL_ADDRESS, - makerAssetData: deployment.assetDataEncoder.ERC20Token(makerToken.address).getABIEncodedTransactionData(), - takerAssetData: deployment.assetDataEncoder.ERC20Token(takerToken.address).getABIEncodedTransactionData(), - makerFeeAssetData: deployment.assetDataEncoder - .ERC20Token(makerToken.address) - .getABIEncodedTransactionData(), - takerFeeAssetData: deployment.assetDataEncoder - .ERC20Token(takerToken.address) - .getABIEncodedTransactionData(), - makerFee: constants.ZERO_AMOUNT, - takerFee: constants.ZERO_AMOUNT, - }; - - maker = new Maker({ - name: 'maker', - deployment, - orderConfig, - }); - }); - - it('fuzz', async () => { const balanceStore = new BlockchainBalanceStore( { StakingProxy: deployment.staking.stakingProxy.address, @@ -82,9 +59,25 @@ blockchainTests('pool membership fuzz test', env => { }, { erc20: { ZRX: deployment.tokens.zrx } }, ); + const simulationEnvironment = new SimulationEnvironment(deployment, balanceStore); - const simulationEnv = new SimulationEnvironment(deployment, balanceStore, [maker]); - const simulation = new PoolMembershipSimulation(simulationEnv); + const actors = [ + new Maker({ deployment, simulationEnvironment, name: 'Maker 1' }), + new Maker({ deployment, simulationEnvironment, name: 'Maker 2' }), + new Taker({ deployment, simulationEnvironment, name: 'Taker 1' }), + new Taker({ deployment, simulationEnvironment, name: 'Taker 2' }), + new MakerTaker({ deployment, simulationEnvironment, name: 'Maker/Taker' }), + ]; + const takers = filterActorsByRole(actors, Taker); + for (const taker of takers) { + await taker.configureERC20TokenAsync(deployment.tokens.weth, deployment.staking.stakingProxy.address); + } + + for (const actor of actors) { + balanceStore.registerTokenOwner(actor.address, actor.name); + } + + const simulation = new PoolMembershipSimulation(simulationEnvironment); return simulation.fuzzAsync(); }); }); diff --git a/contracts/staking/src/index.ts b/contracts/staking/src/index.ts index 7b2d5b8e54..d3cd891d6e 100644 --- a/contracts/staking/src/index.ts +++ b/contracts/staking/src/index.ts @@ -44,6 +44,7 @@ export { artifacts } from './artifacts'; export { StakingRevertErrors, FixedMathRevertErrors } from '@0x/utils'; export { constants } from './constants'; export { + AggregatedStats, StakeInfo, StakeStatus, StoredBalance, diff --git a/contracts/staking/src/types.ts b/contracts/staking/src/types.ts index 3b1fbed995..914cc57c04 100644 --- a/contracts/staking/src/types.ts +++ b/contracts/staking/src/types.ts @@ -147,12 +147,45 @@ export interface OwnerStakeByStatus { }; } -export interface StakingPool { - operator: string; - operatorShare: number; - delegatedStake: StoredBalance; +interface Fraction { + numerator: BigNumber; + denominator: BigNumber; +} + +export class StakingPool { + public delegatedStake: StoredBalance = new StoredBalance(); + public rewards: BigNumber = constants.ZERO_AMOUNT; + public cumulativeRewards: { + [epoch: string]: Fraction; + } = {}; + public cumulativeRewardsLastStored: string = stakingConstants.INITIAL_EPOCH.toString(); + public stats: { + [epoch: string]: PoolStats; + } = {}; + + constructor(public readonly operator: string, public operatorShare: number) {} + + public finalize(): void {} + public creditProtocolFee(): void {} + public withdrawDelegatorRewards(delegator: string): void {} + public delegateStake(delegator: string, amount: BigNumber): void {} + public undelegateStake(delegator: string, amount: BigNumber): void {} } export interface StakingPoolById { [poolId: string]: StakingPool; } + +export interface PoolStats { + feesCollected: BigNumber; + weightedStake: BigNumber; + membersStake: BigNumber; +} + +export interface AggregatedStats { + rewardsAvailable: BigNumber; + numPoolsToFinalize: BigNumber; + totalFeesCollected: BigNumber; + totalWeightedStake: BigNumber; + totalRewardsFinalized: BigNumber; +}