Introduce actor mixin pattern

This commit is contained in:
Michael Zhu 2019-10-17 17:05:21 -07:00
parent 93b02e93b9
commit 4210477e71
6 changed files with 197 additions and 34 deletions

View File

@ -0,0 +1,54 @@
import { DummyERC20TokenContract, WETH9Contract } from '@0x/contracts-erc20';
import { constants } from '@0x/contracts-test-utils';
import { BigNumber } from '@0x/utils';
import { DeploymentManager } from '../deployment/deployment_mananger';
export type Constructor<T = {}> = new (...args: any[]) => T;
export interface ActorConfig {
address: string;
name?: string;
deployment: DeploymentManager;
[subclassProperty: string]: any;
}
export class Actor {
public readonly address: string;
public readonly name: string;
public readonly deployment: DeploymentManager;
constructor(config: ActorConfig) {
this.address = config.address;
this.name = config.name || config.address;
this.deployment = config.deployment;
}
/**
* Sets a balance for an ERC20 token and approves a spender (defaults to the ERC20 asset proxy)
* to transfer the token.
*/
public async configureERC20TokenAsync(
token: DummyERC20TokenContract | WETH9Contract,
spender?: string,
amount?: BigNumber,
): Promise<void> {
if (token instanceof DummyERC20TokenContract) {
await token.setBalance.awaitTransactionSuccessAsync(
this.address,
amount || constants.INITIAL_ERC20_BALANCE,
);
} else {
await token.deposit.awaitTransactionSuccessAsync({
from: this.address,
value: amount || constants.ONE_ETHER,
});
}
await token.approve.awaitTransactionSuccessAsync(
spender || this.deployment.assetProxies.erc20Proxy.address,
constants.MAX_UINT256,
{ from: this.address },
);
}
}

View File

@ -0,0 +1,5 @@
import { Actor } from './base';
import { Maker } from './maker';
import { PoolOperator } from './pool_operator';
export const OperatorAndMaker = PoolOperator(Maker(Actor));

View File

@ -0,0 +1,3 @@
export { MakerActor } from './maker';
export { PoolOperatorActor } from './pool_operator';
export * from './hybrids';

View File

@ -0,0 +1,52 @@
import { constants, OrderFactory } from '@0x/contracts-test-utils';
import { Order, SignedOrder } from '@0x/types';
import { TransactionReceiptWithDecodedLogs } from 'ethereum-types';
import { Actor, ActorConfig, Constructor } from './base';
export interface MakerConfig extends ActorConfig {
orderConfig: Partial<Order>;
}
export function Maker<TBase extends Constructor>(Base: TBase) {
return class extends Base {
public readonly actor: Actor;
public readonly orderFactory: OrderFactory;
constructor(...args: any[]) {
super(...args);
this.actor = (this as any) as Actor;
const { orderConfig } = args[0] as MakerConfig;
const defaultOrderParams = {
...constants.STATIC_ORDER_PARAMS,
makerAddress: this.actor.address,
exchangeAddress: this.actor.deployment.exchange.address,
chainId: this.actor.deployment.chainId,
...orderConfig,
};
const privateKey =
constants.TESTRPC_PRIVATE_KEYS[this.actor.deployment.accounts.indexOf(this.actor.address)];
this.orderFactory = new OrderFactory(privateKey, defaultOrderParams);
}
/**
* Signs an order (optionally, with custom parameters) as the maker.
*/
public async signOrderAsync(customOrderParams: Partial<Order> = {}): Promise<SignedOrder> {
return this.orderFactory.newSignedOrderAsync(customOrderParams);
}
/**
* Joins the staking pool specified by the given ID.
*/
public async joinStakingPoolAsync(poolId: string): Promise<TransactionReceiptWithDecodedLogs> {
const stakingContract = this.actor.deployment.staking.stakingWrapper;
return stakingContract.joinStakingPoolAsMaker.awaitTransactionSuccessAsync(poolId, {
from: this.actor.address,
});
}
};
}
export const MakerActor = Maker(Actor);

View File

@ -0,0 +1,60 @@
import { TransactionReceiptWithDecodedLogs } from 'ethereum-types';
import { Actor, ActorConfig, Constructor } from './base';
export interface PoolOperatorConfig extends ActorConfig {
operatorShare: number;
}
export function PoolOperator<TBase extends Constructor>(Base: TBase) {
return class extends Base {
public readonly operatorShare: number;
public readonly poolIds: string[] = [];
public readonly actor: Actor;
constructor(...args: any[]) {
super(...args);
this.actor = (this as any) as Actor;
const { operatorShare } = args[0] as PoolOperatorConfig;
this.operatorShare = operatorShare;
}
/**
* Creates a staking pool and returns the ID of the new pool.
*/
public async createStakingPoolAsync(
operatorShare: number,
addOperatorAsMaker: boolean = false,
): Promise<string> {
const stakingContract = this.actor.deployment.staking.stakingWrapper;
const txReceipt = await stakingContract.createStakingPool.awaitTransactionSuccessAsync(
operatorShare,
addOperatorAsMaker,
{ from: this.actor.address },
);
const createStakingPoolLog = txReceipt.logs[0];
const poolId = (createStakingPoolLog as any).args.poolId;
this.poolIds.push(poolId);
return poolId;
}
/**
* Joins the staking pool specified by the given ID.
*/
public async decreaseOperatorShareAsync(
poolId: string,
newOperatorShare: number,
): Promise<TransactionReceiptWithDecodedLogs> {
const stakingContract = this.actor.deployment.staking.stakingWrapper;
return stakingContract.decreaseStakingPoolOperatorShare.awaitTransactionSuccessAsync(
poolId,
newOperatorShare,
{ from: this.actor.address },
);
}
};
}
export const PoolOperatorActor = PoolOperator(Actor);

View File

@ -124,12 +124,6 @@ export interface DeploymentOptions {
export class DeploymentManager {
public static protocolFeeMultiplier = new BigNumber(150000);
public assetProxies: AssetProxyContracts;
public governor: ZeroExGovernorContract;
public exchange: ExchangeContract;
public staking: StakingContracts;
public tokens: TokenContracts;
/**
* Fully deploy the 0x exchange and staking contracts and configure the system with the
* asset proxy owner multisig.
@ -141,6 +135,8 @@ export class DeploymentManager {
options: Partial<DeploymentOptions> = {},
): Promise<DeploymentManager> {
const chainId = await environment.getChainIdAsync();
const accounts = await environment.getAccountAddressesAsync();
const owner = options.owner || (await environment.getAccountAddressesAsync())[0];
const txDefaults = {
...environment.txDefaults,
@ -204,7 +200,7 @@ export class DeploymentManager {
staking.stakingProxy,
]);
return new DeploymentManager(assetProxies, governor, exchange, staking, tokens);
return new DeploymentManager(assetProxies, governor, exchange, staking, tokens, chainId, accounts);
}
/**
@ -411,21 +407,18 @@ export class DeploymentManager {
txDefaults: Partial<TxData>,
options: Partial<DeploymentOptions>,
): Promise<TokenContracts> {
const numErc20TokensToDeploy = _.get(
options,
['numErc20TokensToDeploy'],
constants.NUM_DUMMY_ERC20_CONTRACTS_TO_DEPLOY,
);
const numErc721TokensToDeploy = _.get(
options,
['numErc721TokensToDeploy'],
constants.NUM_DUMMY_ERC721_CONTRACTS_TO_DEPLOY,
);
const numErc1155TokensToDeploy = _.get(
options,
['numErc1155TokensToDeploy'],
constants.NUM_DUMMY_ERC1155_CONTRACTS_TO_DEPLOY,
);
const numErc20TokensToDeploy =
options.numErc20TokensToDeploy !== undefined
? options.numErc20TokensToDeploy
: constants.NUM_DUMMY_ERC20_TO_DEPLOY;
const numErc721TokensToDeploy =
options.numErc721TokensToDeploy !== undefined
? options.numErc721TokensToDeploy
: constants.NUM_DUMMY_ERC721_TO_DEPLOY;
const numErc1155TokensToDeploy =
options.numErc1155TokensToDeploy !== undefined
? options.numErc1155TokensToDeploy
: constants.NUM_DUMMY_ERC1155_CONTRACTS_TO_DEPLOY;
const erc20 = await Promise.all(
_.times(
@ -493,16 +486,12 @@ export class DeploymentManager {
}
constructor(
assetProxies: AssetProxyContracts,
governor: ZeroExGovernorContract,
exchange: ExchangeContract,
staking: StakingContracts,
tokens: TokenContracts,
) {
this.assetProxies = assetProxies;
this.governor = governor;
this.exchange = exchange;
this.staking = staking;
this.tokens = tokens;
}
public assetProxies: AssetProxyContracts,
public governor: ZeroExGovernorContract,
public exchange: ExchangeContract,
public staking: StakingContracts,
public tokens: TokenContracts,
public chainId: number,
public accounts: string[],
) {}
}