Made function assertions work with the new wrappers
This commit is contained in:
@@ -16,10 +16,10 @@ import { FunctionAssertion, FunctionResult } from './function_assertion';
|
||||
export function validCreateStakingPoolAssertion(
|
||||
deployment: DeploymentManager,
|
||||
pools: StakingPoolById,
|
||||
): FunctionAssertion<string, string> {
|
||||
): FunctionAssertion<[number, boolean], string, string> {
|
||||
const { stakingWrapper } = deployment.staking;
|
||||
|
||||
return new FunctionAssertion(stakingWrapper.createStakingPool, {
|
||||
return new FunctionAssertion<[number, boolean], string, string>(stakingWrapper.createStakingPool, {
|
||||
// Returns the expected ID of th created pool
|
||||
before: async () => {
|
||||
const lastPoolId = await stakingWrapper.lastPoolId().callAsync();
|
||||
@@ -32,23 +32,31 @@ export function validCreateStakingPoolAssertion(
|
||||
after: async (
|
||||
expectedPoolId: string,
|
||||
result: FunctionResult,
|
||||
operatorShare: number,
|
||||
addOperatorAsMaker: boolean,
|
||||
txData: Partial<TxData>,
|
||||
args: {
|
||||
args: [number, boolean];
|
||||
txData: Partial<TxData>;
|
||||
},
|
||||
) => {
|
||||
logUtils.log(`createStakingPool(${operatorShare}, ${addOperatorAsMaker}) => ${expectedPoolId}`);
|
||||
console.log(100);
|
||||
logUtils.log(`createStakingPool(${args.args[0]}, ${args.args[1]}) => ${expectedPoolId}`);
|
||||
console.log(101);
|
||||
|
||||
// Checks the logs for the new poolId, verifies that it is as expected
|
||||
console.log(result.receipt);
|
||||
const log = result.receipt!.logs[0]; // tslint:disable-line:no-non-null-assertion
|
||||
console.log(102);
|
||||
const actualPoolId = (log as any).args.poolId;
|
||||
console.log(103);
|
||||
expect(actualPoolId).to.equal(expectedPoolId);
|
||||
console.log(104);
|
||||
|
||||
// Adds the new pool to local state
|
||||
pools[actualPoolId] = {
|
||||
operator: txData.from as string,
|
||||
operatorShare,
|
||||
operator: args.txData.from as string,
|
||||
operatorShare: args.args[0],
|
||||
delegatedStake: new StoredBalance(),
|
||||
};
|
||||
console.log(105);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
@@ -13,11 +13,14 @@ import { FunctionAssertion, FunctionResult } from './function_assertion';
|
||||
export function validDecreaseStakingPoolOperatorShareAssertion(
|
||||
deployment: DeploymentManager,
|
||||
pools: StakingPoolById,
|
||||
): FunctionAssertion<{}, void> {
|
||||
): FunctionAssertion<[string, number], {}, void> {
|
||||
const { stakingWrapper } = deployment.staking;
|
||||
|
||||
return new FunctionAssertion<{}, void>(stakingWrapper.decreaseStakingPoolOperatorShare, {
|
||||
after: async (_beforeInfo, _result: FunctionResult, poolId: string, expectedOperatorShare: number) => {
|
||||
return new FunctionAssertion<[string, number], {}, void>(stakingWrapper.decreaseStakingPoolOperatorShare, {
|
||||
after: async (_beforeInfo, _result: FunctionResult, args: { args: [string, number] }) => {
|
||||
const poolId = args.args[0];
|
||||
const expectedOperatorShare = args.args[1];
|
||||
|
||||
logUtils.log(`decreaseStakingPoolOperatorShare(${poolId}, ${expectedOperatorShare})`);
|
||||
|
||||
// Checks that the on-chain pool's operator share has been updated.
|
||||
|
@@ -1,11 +1,16 @@
|
||||
import { ContractFunctionObj, ContractTxFunctionObj } from '@0x/base-contract';
|
||||
import { TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
||||
import { TransactionReceiptWithDecodedLogs, TxData } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
// tslint:disable:max-classes-per-file
|
||||
|
||||
export type GenericContractFunction<T> = (...args: any[]) => ContractFunctionObj<T>;
|
||||
|
||||
export interface FunctionArguments<TArgs extends any[]> {
|
||||
args: TArgs;
|
||||
txData: Partial<TxData>;
|
||||
}
|
||||
|
||||
export interface FunctionResult {
|
||||
data?: any;
|
||||
success: boolean;
|
||||
@@ -22,9 +27,9 @@ export interface FunctionResult {
|
||||
* @param after A function that will be run after a call to the contract wrapper
|
||||
* function.
|
||||
*/
|
||||
export interface Condition<TBefore> {
|
||||
before: (...args: any[]) => Promise<TBefore>;
|
||||
after: (beforeInfo: TBefore, result: FunctionResult, ...args: any[]) => Promise<any>;
|
||||
export interface Condition<TArgs extends any[], TBefore> {
|
||||
before: (args: FunctionArguments<TArgs>) => Promise<TBefore>;
|
||||
after: (beforeInfo: TBefore, result: FunctionResult, args: FunctionArguments<TArgs>) => Promise<any>;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -34,8 +39,8 @@ export interface Condition<TBefore> {
|
||||
* our `Assertion` implementations will do in practice).
|
||||
* @param runAsync The function to execute for the assertion.
|
||||
*/
|
||||
export interface Assertion {
|
||||
executeAsync: (...args: any[]) => Promise<any>;
|
||||
export interface Assertion<TArgs extends any[]> {
|
||||
executeAsync: (args: FunctionArguments<TArgs>) => Promise<any>;
|
||||
}
|
||||
|
||||
export interface AssertionResult<TBefore = unknown> {
|
||||
@@ -47,9 +52,9 @@ export interface AssertionResult<TBefore = unknown> {
|
||||
* This class implements `Assertion` and represents a "Hoare Triple" that can be
|
||||
* executed.
|
||||
*/
|
||||
export class FunctionAssertion<TBefore, ReturnDataType> implements Assertion {
|
||||
export class FunctionAssertion<TArgs extends any[], TBefore, ReturnDataType> implements Assertion<TArgs> {
|
||||
// A condition that will be applied to `wrapperFunction`.
|
||||
public condition: Condition<TBefore>;
|
||||
public condition: Condition<TArgs, TBefore>;
|
||||
|
||||
// The wrapper function that will be wrapped in assertions.
|
||||
public wrapperFunction: (
|
||||
@@ -60,11 +65,15 @@ export class FunctionAssertion<TBefore, ReturnDataType> implements Assertion {
|
||||
wrapperFunction: (
|
||||
...args: any[] // tslint:disable-line:trailing-comma
|
||||
) => ContractTxFunctionObj<ReturnDataType> | ContractFunctionObj<ReturnDataType>,
|
||||
condition: Partial<Condition<TBefore>> = {},
|
||||
condition: Partial<Condition<TArgs, TBefore>> = {},
|
||||
) {
|
||||
this.condition = {
|
||||
before: _.noop.bind(this),
|
||||
after: _.noop.bind(this),
|
||||
before: async (args: FunctionArguments<TArgs>) => {
|
||||
return ({} as any) as TBefore;
|
||||
},
|
||||
after: async (beforeInfo: TBefore, result: FunctionResult, args: FunctionArguments<TArgs>) => {
|
||||
return ({} as any) as TBefore;
|
||||
},
|
||||
...condition,
|
||||
};
|
||||
this.wrapperFunction = wrapperFunction;
|
||||
@@ -74,9 +83,9 @@ export class FunctionAssertion<TBefore, ReturnDataType> implements Assertion {
|
||||
* Runs the wrapped function and fails if the before or after assertions fail.
|
||||
* @param ...args The args to the contract wrapper function.
|
||||
*/
|
||||
public async executeAsync(...args: any[]): Promise<AssertionResult<TBefore>> {
|
||||
public async executeAsync(args: FunctionArguments<TArgs>): Promise<AssertionResult<TBefore>> {
|
||||
// Call the before condition.
|
||||
const beforeInfo = await this.condition.before(...args);
|
||||
const beforeInfo = await this.condition.before(args);
|
||||
|
||||
// Initialize the callResult so that the default success value is true.
|
||||
const callResult: FunctionResult = { success: true };
|
||||
@@ -84,21 +93,27 @@ export class FunctionAssertion<TBefore, ReturnDataType> implements Assertion {
|
||||
// Try to make the call to the function. If it is successful, pass the
|
||||
// result and receipt to the after condition.
|
||||
try {
|
||||
const functionWithArgs = this.wrapperFunction(...args) as ContractTxFunctionObj<ReturnDataType>;
|
||||
callResult.data = await functionWithArgs.callAsync();
|
||||
const functionWithArgs = this.wrapperFunction(...args.args) as ContractTxFunctionObj<ReturnDataType>;
|
||||
callResult.data = await functionWithArgs.callAsync(args.txData);
|
||||
|
||||
console.log(functionWithArgs);
|
||||
|
||||
callResult.receipt =
|
||||
functionWithArgs.awaitTransactionSuccessAsync !== undefined
|
||||
? await functionWithArgs.awaitTransactionSuccessAsync() // tslint:disable-line:await-promise
|
||||
? await functionWithArgs.awaitTransactionSuccessAsync(args.txData) // tslint:disable-line:await-promise
|
||||
: undefined;
|
||||
// tslint:enable:await-promise
|
||||
} catch (error) {
|
||||
console.log('got here');
|
||||
console.log(error);
|
||||
|
||||
callResult.data = error;
|
||||
callResult.success = false;
|
||||
callResult.receipt = undefined;
|
||||
}
|
||||
|
||||
// Call the after condition.
|
||||
const afterInfo = await this.condition.after(beforeInfo, callResult, ...args);
|
||||
const afterInfo = await this.condition.after(beforeInfo, callResult, args);
|
||||
|
||||
return {
|
||||
beforeInfo,
|
||||
|
@@ -0,0 +1,45 @@
|
||||
import { StakingEvents, StakingMakerStakingPoolSetEventArgs, StakingPoolById } from '@0x/contracts-staking';
|
||||
import { constants, expect, filterLogsToArguments } from '@0x/contracts-test-utils';
|
||||
import { logUtils } from '@0x/utils';
|
||||
|
||||
import { DeploymentManager } from '../deployment_manager';
|
||||
|
||||
import { FunctionArguments, FunctionAssertion, FunctionResult } from './function_assertion';
|
||||
|
||||
export function validJoinStakingPoolAssertion(deployment: DeploymentManager): FunctionAssertion<[string], {}, void> {
|
||||
return new FunctionAssertion<[string], {}, void>(deployment.staking.stakingWrapper.joinStakingPoolAsMaker, {
|
||||
after: async (_beforeInfo, _result: FunctionResult, args: FunctionArguments<[string]>) => {
|
||||
const poolId = args.args[0];
|
||||
|
||||
if (args.txData === undefined) {
|
||||
throw new Error('Undefined transaction data');
|
||||
}
|
||||
|
||||
if (args.txData.from === undefined) {
|
||||
throw new Error('Undefined from address');
|
||||
}
|
||||
|
||||
if (_result.receipt === undefined) {
|
||||
throw new Error('Undefined transaction receipt');
|
||||
}
|
||||
|
||||
expect(_result.success).to.be.true();
|
||||
|
||||
const logs = _result.receipt.logs;
|
||||
const logArgs = filterLogsToArguments<StakingMakerStakingPoolSetEventArgs>(
|
||||
logs,
|
||||
StakingEvents.MakerStakingPoolSet,
|
||||
);
|
||||
expect(logArgs).to.be.deep.eq([
|
||||
{
|
||||
maker: args.txData.from,
|
||||
poolId,
|
||||
},
|
||||
]);
|
||||
const joinedPoolId = deployment.staking.stakingWrapper.poolIdByMaker(args.txData.from);
|
||||
expect(joinedPoolId).to.be.eq(poolId);
|
||||
|
||||
console.log(`Pool ${poolId} joined by ${args.txData.from}`); /* tslint:disable-line:no-console */
|
||||
},
|
||||
});
|
||||
}
|
@@ -13,7 +13,7 @@ import * as _ from 'lodash';
|
||||
|
||||
import { DeploymentManager } from '../deployment_manager';
|
||||
|
||||
import { FunctionAssertion } from './function_assertion';
|
||||
import { FunctionArguments, FunctionAssertion, FunctionResult } from './function_assertion';
|
||||
|
||||
function incrementNextEpochBalance(stakeBalance: StoredBalance, amount: BigNumber): void {
|
||||
_.update(stakeBalance, ['nextEpochBalance'], balance => (balance || constants.ZERO_AMOUNT).plus(amount));
|
||||
@@ -82,25 +82,24 @@ export function validMoveStakeAssertion(
|
||||
globalStake: GlobalStakeByStatus,
|
||||
ownerStake: OwnerStakeByStatus,
|
||||
pools: StakingPoolById,
|
||||
): FunctionAssertion<{}, void> {
|
||||
): FunctionAssertion<[StakeInfo, StakeInfo, BigNumber], {}, void> {
|
||||
const { stakingWrapper } = deployment.staking;
|
||||
|
||||
return new FunctionAssertion<{}, void>(stakingWrapper.moveStake, {
|
||||
return new FunctionAssertion<[StakeInfo, StakeInfo, BigNumber], {}, void>(stakingWrapper.moveStake, {
|
||||
after: async (
|
||||
_beforeInfo,
|
||||
_result,
|
||||
from: StakeInfo,
|
||||
to: StakeInfo,
|
||||
amount: BigNumber,
|
||||
txData: Partial<TxData>,
|
||||
_beforeInfo: {},
|
||||
_result: FunctionResult,
|
||||
args: FunctionArguments<[StakeInfo, StakeInfo, BigNumber]>,
|
||||
) => {
|
||||
const [from, to, amount] = args.args;
|
||||
|
||||
logUtils.log(
|
||||
`moveStake({status: ${StakeStatus[from.status]}, poolId: ${from.poolId} }, { status: ${
|
||||
StakeStatus[to.status]
|
||||
}, poolId: ${to.poolId} }, ${amount})`,
|
||||
);
|
||||
|
||||
const owner = txData.from as string;
|
||||
const owner = args.txData.from as string;
|
||||
|
||||
// Update local balances to match the expected result of this `moveStake` operation
|
||||
const updatedPools = updateNextEpochBalances(globalStake, ownerStake, pools, from, to, amount);
|
||||
|
@@ -7,7 +7,7 @@ import { BlockchainBalanceStore } from '../balances/blockchain_balance_store';
|
||||
import { LocalBalanceStore } from '../balances/local_balance_store';
|
||||
import { DeploymentManager } from '../deployment_manager';
|
||||
|
||||
import { FunctionAssertion, FunctionResult } from './function_assertion';
|
||||
import { FunctionArguments, FunctionAssertion, FunctionResult } from './function_assertion';
|
||||
|
||||
function expectedUndelegatedStake(
|
||||
initStake: OwnerStakeByStatus | GlobalStakeByStatus,
|
||||
@@ -30,15 +30,17 @@ export function validStakeAssertion(
|
||||
balanceStore: BlockchainBalanceStore,
|
||||
globalStake: GlobalStakeByStatus,
|
||||
ownerStake: OwnerStakeByStatus,
|
||||
): FunctionAssertion<LocalBalanceStore, void> {
|
||||
): FunctionAssertion<[BigNumber], LocalBalanceStore, void> {
|
||||
const { stakingWrapper, zrxVault } = deployment.staking;
|
||||
|
||||
return new FunctionAssertion(stakingWrapper.stake, {
|
||||
before: async (amount: BigNumber, txData: Partial<TxData>) => {
|
||||
before: async (args: FunctionArguments<[BigNumber]>) => {
|
||||
const [amount] = args.args;
|
||||
|
||||
// Simulates the transfer of ZRX from staker to vault
|
||||
const expectedBalances = LocalBalanceStore.create(balanceStore);
|
||||
expectedBalances.transferAsset(
|
||||
txData.from as string,
|
||||
args.txData.from as string,
|
||||
zrxVault.address,
|
||||
amount,
|
||||
deployment.assetDataEncoder.ERC20Token(deployment.tokens.zrx.address).getABIEncodedTransactionData(),
|
||||
@@ -48,9 +50,10 @@ export function validStakeAssertion(
|
||||
after: async (
|
||||
expectedBalances: LocalBalanceStore,
|
||||
_result: FunctionResult,
|
||||
amount: BigNumber,
|
||||
txData: Partial<TxData>,
|
||||
args: FunctionArguments<[BigNumber]>,
|
||||
) => {
|
||||
const [amount] = args.args;
|
||||
|
||||
logUtils.log(`stake(${amount})`);
|
||||
|
||||
// Checks that the ZRX transfer updated balances as expected.
|
||||
@@ -59,7 +62,7 @@ export function validStakeAssertion(
|
||||
|
||||
// Checks that the owner's undelegated stake has increased by the stake amount
|
||||
const ownerUndelegatedStake = await stakingWrapper
|
||||
.getOwnerStakeByStatus(txData.from as string, StakeStatus.Undelegated)
|
||||
.getOwnerStakeByStatus(args.txData.from as string, StakeStatus.Undelegated)
|
||||
.callAsync();
|
||||
const expectedOwnerUndelegatedStake = expectedUndelegatedStake(ownerStake, amount);
|
||||
expect(ownerUndelegatedStake, 'Owner undelegated stake').to.deep.equal(expectedOwnerUndelegatedStake);
|
||||
|
@@ -7,7 +7,7 @@ import { BlockchainBalanceStore } from '../balances/blockchain_balance_store';
|
||||
import { LocalBalanceStore } from '../balances/local_balance_store';
|
||||
import { DeploymentManager } from '../deployment_manager';
|
||||
|
||||
import { FunctionAssertion, FunctionResult } from './function_assertion';
|
||||
import { FunctionArguments, FunctionAssertion, FunctionResult } from './function_assertion';
|
||||
|
||||
function expectedUndelegatedStake(
|
||||
initStake: OwnerStakeByStatus | GlobalStakeByStatus,
|
||||
@@ -30,16 +30,18 @@ export function validUnstakeAssertion(
|
||||
balanceStore: BlockchainBalanceStore,
|
||||
globalStake: GlobalStakeByStatus,
|
||||
ownerStake: OwnerStakeByStatus,
|
||||
): FunctionAssertion<LocalBalanceStore, void> {
|
||||
): FunctionAssertion<[BigNumber], LocalBalanceStore, void> {
|
||||
const { stakingWrapper, zrxVault } = deployment.staking;
|
||||
|
||||
return new FunctionAssertion(stakingWrapper.unstake, {
|
||||
before: async (amount: BigNumber, txData: Partial<TxData>) => {
|
||||
before: async (args: FunctionArguments<[BigNumber]>) => {
|
||||
const [amount] = args.args;
|
||||
|
||||
// Simulates the transfer of ZRX from vault to staker
|
||||
const expectedBalances = LocalBalanceStore.create(balanceStore);
|
||||
expectedBalances.transferAsset(
|
||||
zrxVault.address,
|
||||
txData.from as string,
|
||||
args.txData.from as string,
|
||||
amount,
|
||||
deployment.assetDataEncoder.ERC20Token(deployment.tokens.zrx.address).getABIEncodedTransactionData(),
|
||||
);
|
||||
@@ -48,9 +50,10 @@ export function validUnstakeAssertion(
|
||||
after: async (
|
||||
expectedBalances: LocalBalanceStore,
|
||||
_result: FunctionResult,
|
||||
amount: BigNumber,
|
||||
txData: Partial<TxData>,
|
||||
args: FunctionArguments<[BigNumber]>,
|
||||
) => {
|
||||
const [amount] = args.args;
|
||||
|
||||
logUtils.log(`unstake(${amount})`);
|
||||
|
||||
// Checks that the ZRX transfer updated balances as expected.
|
||||
@@ -59,7 +62,7 @@ export function validUnstakeAssertion(
|
||||
|
||||
// Checks that the owner's undelegated stake has decreased by the stake amount
|
||||
const ownerUndelegatedStake = await stakingWrapper
|
||||
.getOwnerStakeByStatus(txData.from as string, StakeStatus.Undelegated)
|
||||
.getOwnerStakeByStatus(args.txData.from as string, StakeStatus.Undelegated)
|
||||
.callAsync();
|
||||
const expectedOwnerUndelegatedStake = expectedUndelegatedStake(ownerStake, amount);
|
||||
expect(ownerUndelegatedStake, 'Owner undelegated stake').to.deep.equal(expectedOwnerUndelegatedStake);
|
||||
|
Reference in New Issue
Block a user