From 4210477e7122423ed68c5fc8d0169de140f3f107 Mon Sep 17 00:00:00 2001 From: Michael Zhu Date: Thu, 17 Oct 2019 17:05:21 -0700 Subject: [PATCH] Introduce actor mixin pattern --- contracts/integrations/test/actors/base.ts | 54 +++++++++++++++++ contracts/integrations/test/actors/hybrids.ts | 5 ++ contracts/integrations/test/actors/index.ts | 3 + contracts/integrations/test/actors/maker.ts | 52 ++++++++++++++++ .../integrations/test/actors/pool_operator.ts | 60 +++++++++++++++++++ .../test/deployment/deployment_mananger.ts | 57 +++++++----------- 6 files changed, 197 insertions(+), 34 deletions(-) create mode 100644 contracts/integrations/test/actors/base.ts create mode 100644 contracts/integrations/test/actors/hybrids.ts create mode 100644 contracts/integrations/test/actors/index.ts create mode 100644 contracts/integrations/test/actors/maker.ts create mode 100644 contracts/integrations/test/actors/pool_operator.ts diff --git a/contracts/integrations/test/actors/base.ts b/contracts/integrations/test/actors/base.ts new file mode 100644 index 0000000000..d3933fbd33 --- /dev/null +++ b/contracts/integrations/test/actors/base.ts @@ -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 = 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 { + 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 }, + ); + } +} diff --git a/contracts/integrations/test/actors/hybrids.ts b/contracts/integrations/test/actors/hybrids.ts new file mode 100644 index 0000000000..221b9243d6 --- /dev/null +++ b/contracts/integrations/test/actors/hybrids.ts @@ -0,0 +1,5 @@ +import { Actor } from './base'; +import { Maker } from './maker'; +import { PoolOperator } from './pool_operator'; + +export const OperatorAndMaker = PoolOperator(Maker(Actor)); diff --git a/contracts/integrations/test/actors/index.ts b/contracts/integrations/test/actors/index.ts new file mode 100644 index 0000000000..af721ca484 --- /dev/null +++ b/contracts/integrations/test/actors/index.ts @@ -0,0 +1,3 @@ +export { MakerActor } from './maker'; +export { PoolOperatorActor } from './pool_operator'; +export * from './hybrids'; diff --git a/contracts/integrations/test/actors/maker.ts b/contracts/integrations/test/actors/maker.ts new file mode 100644 index 0000000000..636cab585e --- /dev/null +++ b/contracts/integrations/test/actors/maker.ts @@ -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; +} + +export function Maker(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 = {}): Promise { + return this.orderFactory.newSignedOrderAsync(customOrderParams); + } + + /** + * Joins the staking pool specified by the given ID. + */ + public async joinStakingPoolAsync(poolId: string): Promise { + const stakingContract = this.actor.deployment.staking.stakingWrapper; + return stakingContract.joinStakingPoolAsMaker.awaitTransactionSuccessAsync(poolId, { + from: this.actor.address, + }); + } + }; +} + +export const MakerActor = Maker(Actor); diff --git a/contracts/integrations/test/actors/pool_operator.ts b/contracts/integrations/test/actors/pool_operator.ts new file mode 100644 index 0000000000..d2611ff4a4 --- /dev/null +++ b/contracts/integrations/test/actors/pool_operator.ts @@ -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(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 { + 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 { + const stakingContract = this.actor.deployment.staking.stakingWrapper; + return stakingContract.decreaseStakingPoolOperatorShare.awaitTransactionSuccessAsync( + poolId, + newOperatorShare, + { from: this.actor.address }, + ); + } + }; +} + +export const PoolOperatorActor = PoolOperator(Actor); diff --git a/contracts/integrations/test/deployment/deployment_mananger.ts b/contracts/integrations/test/deployment/deployment_mananger.ts index 03b4cbb324..dc09aa3f1e 100644 --- a/contracts/integrations/test/deployment/deployment_mananger.ts +++ b/contracts/integrations/test/deployment/deployment_mananger.ts @@ -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 = {}, ): Promise { 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, options: Partial, ): Promise { - 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[], + ) {} }