Flesh out fillOrder integration tests
This commit is contained in:
parent
8aa69233e0
commit
7aa88307f6
@ -19,7 +19,7 @@ export class BlockchainBalanceStore extends BalanceStore {
|
|||||||
public constructor(
|
public constructor(
|
||||||
tokenOwnersByName: TokenOwnersByName,
|
tokenOwnersByName: TokenOwnersByName,
|
||||||
tokenContractsByName: Partial<TokenContractsByName>,
|
tokenContractsByName: Partial<TokenContractsByName>,
|
||||||
tokenIds: Partial<TokenIds>,
|
tokenIds: Partial<TokenIds> = {},
|
||||||
) {
|
) {
|
||||||
super(tokenOwnersByName, tokenContractsByName);
|
super(tokenOwnersByName, tokenContractsByName);
|
||||||
this._tokenContracts = {
|
this._tokenContracts = {
|
||||||
|
@ -2,4 +2,3 @@ export * from './artifacts';
|
|||||||
export * from './wrappers';
|
export * from './wrappers';
|
||||||
export * from '../test/utils/function_assertions';
|
export * from '../test/utils/function_assertions';
|
||||||
export * from '../test/utils/deployment_manager';
|
export * from '../test/utils/deployment_manager';
|
||||||
export * from '../test/utils/address_manager';
|
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
import { Actor } from './base';
|
import { Actor } from './base';
|
||||||
import { MakerMixin } from './maker';
|
import { MakerMixin } from './maker';
|
||||||
import { PoolOperatorMixin } from './pool_operator';
|
import { PoolOperatorMixin } from './pool_operator';
|
||||||
|
import { StakerMixin } from './staker';
|
||||||
|
import { KeeperMixin } from './keeper';
|
||||||
|
|
||||||
export class OperatorMaker extends PoolOperatorMixin(MakerMixin(Actor)) {}
|
export class OperatorMaker extends PoolOperatorMixin(MakerMixin(Actor)) {}
|
||||||
|
export class StakerMaker extends StakerMixin(MakerMixin(Actor)) {}
|
||||||
|
export class StakerOperator extends StakerMixin(PoolOperatorMixin(Actor)) {}
|
||||||
|
export class OperatorStakerMaker extends PoolOperatorMixin(StakerMixin(MakerMixin(Actor))) {}
|
||||||
|
export class StakerKeeper extends StakerMixin(KeeperMixin(Actor)) {}
|
||||||
|
@ -2,5 +2,8 @@ export { Actor } from './base';
|
|||||||
export { Maker } from './maker';
|
export { Maker } from './maker';
|
||||||
export { PoolOperator } from './pool_operator';
|
export { PoolOperator } from './pool_operator';
|
||||||
export { FeeRecipient } from './fee_recipient';
|
export { FeeRecipient } from './fee_recipient';
|
||||||
|
export { Staker } from './staker';
|
||||||
|
export { Keeper } from './keeper';
|
||||||
|
export { Taker } from './taker';
|
||||||
export * from './hybrids';
|
export * from './hybrids';
|
||||||
export * from './utils';
|
export * from './utils';
|
||||||
|
72
contracts/integrations/test/actors/keeper.ts
Normal file
72
contracts/integrations/test/actors/keeper.ts
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import { IStakingEventsStakingPoolEarnedRewardsInEpochEventArgs, TestStakingEvents } from '@0x/contracts-staking';
|
||||||
|
import { filterLogsToArguments, web3Wrapper } from '@0x/contracts-test-utils';
|
||||||
|
import { BigNumber } from '@0x/utils';
|
||||||
|
import { BlockParamLiteral, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
||||||
|
|
||||||
|
import { Actor, Constructor } from './base';
|
||||||
|
|
||||||
|
export function KeeperMixin<TBase extends Constructor>(Base: TBase) {
|
||||||
|
return class extends Base {
|
||||||
|
public readonly actor: Actor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The mixin pattern requires that this constructor uses `...args: any[]`, but this class
|
||||||
|
* really expects a single `Actor` parameter (assuming `Actor` is used as the base
|
||||||
|
* class).
|
||||||
|
*/
|
||||||
|
constructor(...args: any[]) {
|
||||||
|
super(...args);
|
||||||
|
this.actor = (this as any) as Actor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ends the current epoch, fast-forwarding to the end of the epoch by default.
|
||||||
|
*/
|
||||||
|
public async endEpochAsync(shouldFastForward: boolean = true): Promise<TransactionReceiptWithDecodedLogs> {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
return stakingWrapper.endEpoch.awaitTransactionSuccessAsync({ from: this.actor.address });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finalizes staking pools corresponding to the given `poolIds`. If none are provided,
|
||||||
|
* finalizes all pools that earned rewards in the previous epoch.
|
||||||
|
*/
|
||||||
|
public async finalizePoolsAsync(poolIds: string[] = []): Promise<TransactionReceiptWithDecodedLogs[]> {
|
||||||
|
const { stakingWrapper } = this.actor.deployment.staking;
|
||||||
|
// If no poolIds provided, finalize all active pools from the previous epoch
|
||||||
|
if (poolIds.length === 0) {
|
||||||
|
const previousEpoch = (await stakingWrapper.currentEpoch.callAsync()).minus(1);
|
||||||
|
const events = filterLogsToArguments<IStakingEventsStakingPoolEarnedRewardsInEpochEventArgs>(
|
||||||
|
await stakingWrapper.getLogsAsync(
|
||||||
|
TestStakingEvents.StakingPoolEarnedRewardsInEpoch,
|
||||||
|
{ fromBlock: BlockParamLiteral.Earliest, toBlock: BlockParamLiteral.Latest },
|
||||||
|
{ epoch: new BigNumber(previousEpoch) },
|
||||||
|
),
|
||||||
|
TestStakingEvents.StakingPoolEarnedRewardsInEpoch,
|
||||||
|
);
|
||||||
|
poolIds.concat(events.map(event => event.poolId));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.all(
|
||||||
|
poolIds.map(
|
||||||
|
async poolId =>
|
||||||
|
await stakingWrapper.finalizePool.awaitTransactionSuccessAsync(poolId, {
|
||||||
|
from: this.actor.address,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Keeper extends KeeperMixin(Actor) {}
|
@ -10,7 +10,7 @@ export interface MakerConfig extends ActorConfig {
|
|||||||
|
|
||||||
export function MakerMixin<TBase extends Constructor>(Base: TBase) {
|
export function MakerMixin<TBase extends Constructor>(Base: TBase) {
|
||||||
return class extends Base {
|
return class extends Base {
|
||||||
public poolId?: string;
|
public makerPoolId?: string;
|
||||||
public readonly actor: Actor;
|
public readonly actor: Actor;
|
||||||
public readonly orderFactory: OrderFactory;
|
public readonly orderFactory: OrderFactory;
|
||||||
|
|
||||||
@ -56,7 +56,7 @@ export function MakerMixin<TBase extends Constructor>(Base: TBase) {
|
|||||||
*/
|
*/
|
||||||
public async joinStakingPoolAsync(poolId: string): Promise<TransactionReceiptWithDecodedLogs> {
|
public async joinStakingPoolAsync(poolId: string): Promise<TransactionReceiptWithDecodedLogs> {
|
||||||
const stakingContract = this.actor.deployment.staking.stakingWrapper;
|
const stakingContract = this.actor.deployment.staking.stakingWrapper;
|
||||||
this.poolId = poolId;
|
this.makerPoolId = poolId;
|
||||||
return stakingContract.joinStakingPoolAsMaker.awaitTransactionSuccessAsync(poolId, {
|
return stakingContract.joinStakingPoolAsMaker.awaitTransactionSuccessAsync(poolId, {
|
||||||
from: this.actor.address,
|
from: this.actor.address,
|
||||||
});
|
});
|
||||||
|
@ -1,28 +1,24 @@
|
|||||||
import { TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
import { TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
||||||
|
|
||||||
import { Actor, ActorConfig, Constructor } from './base';
|
import { Actor, Constructor } from './base';
|
||||||
|
|
||||||
export interface PoolOperatorConfig extends ActorConfig {
|
export interface OperatorShareByPoolId {
|
||||||
operatorShare: number;
|
[poolId: string]: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PoolOperatorMixin<TBase extends Constructor>(Base: TBase) {
|
export function PoolOperatorMixin<TBase extends Constructor>(Base: TBase) {
|
||||||
return class extends Base {
|
return class extends Base {
|
||||||
public operatorShare: number;
|
public readonly operatorShares: OperatorShareByPoolId = {};
|
||||||
public readonly poolIds: string[] = [];
|
|
||||||
public readonly actor: Actor;
|
public readonly actor: Actor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The mixin pattern requires that this constructor uses `...args: any[]`, but this class
|
* The mixin pattern requires that this constructor uses `...args: any[]`, but this class
|
||||||
* really expects a single `PoolOperatorConfig` parameter (assuming `Actor` is used as the
|
* really expects a single `ActorConfig` parameter (assuming `Actor` is used as the
|
||||||
* base class).
|
* base class).
|
||||||
*/
|
*/
|
||||||
constructor(...args: any[]) {
|
constructor(...args: any[]) {
|
||||||
super(...args);
|
super(...args);
|
||||||
this.actor = (this as any) as Actor;
|
this.actor = (this as any) as Actor;
|
||||||
|
|
||||||
const { operatorShare } = args[0] as PoolOperatorConfig;
|
|
||||||
this.operatorShare = operatorShare;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -41,7 +37,7 @@ export function PoolOperatorMixin<TBase extends Constructor>(Base: TBase) {
|
|||||||
|
|
||||||
const createStakingPoolLog = txReceipt.logs[0];
|
const createStakingPoolLog = txReceipt.logs[0];
|
||||||
const poolId = (createStakingPoolLog as any).args.poolId;
|
const poolId = (createStakingPoolLog as any).args.poolId;
|
||||||
this.poolIds.push(poolId);
|
this.operatorShares[poolId] = operatorShare;
|
||||||
return poolId;
|
return poolId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,7 +49,7 @@ export function PoolOperatorMixin<TBase extends Constructor>(Base: TBase) {
|
|||||||
newOperatorShare: number,
|
newOperatorShare: number,
|
||||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
): Promise<TransactionReceiptWithDecodedLogs> {
|
||||||
const stakingContract = this.actor.deployment.staking.stakingWrapper;
|
const stakingContract = this.actor.deployment.staking.stakingWrapper;
|
||||||
this.operatorShare = newOperatorShare;
|
this.operatorShares[poolId] = newOperatorShare;
|
||||||
return stakingContract.decreaseStakingPoolOperatorShare.awaitTransactionSuccessAsync(
|
return stakingContract.decreaseStakingPoolOperatorShare.awaitTransactionSuccessAsync(
|
||||||
poolId,
|
poolId,
|
||||||
newOperatorShare,
|
newOperatorShare,
|
||||||
|
41
contracts/integrations/test/actors/staker.ts
Normal file
41
contracts/integrations/test/actors/staker.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { StakeInfo, StakeStatus } from '@0x/contracts-staking';
|
||||||
|
import { BigNumber } from '@0x/utils';
|
||||||
|
|
||||||
|
import { Actor, Constructor } from './base';
|
||||||
|
|
||||||
|
export function StakerMixin<TBase extends Constructor>(Base: TBase) {
|
||||||
|
return class extends Base {
|
||||||
|
public readonly actor: Actor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The mixin pattern requires that this constructor uses `...args: any[]`, but this class
|
||||||
|
* really expects a single `ActorConfig` parameter (assuming `Actor` is used as the base
|
||||||
|
* class).
|
||||||
|
*/
|
||||||
|
constructor(...args: any[]) {
|
||||||
|
super(...args);
|
||||||
|
this.actor = (this as any) as Actor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stakes the given amount of ZRX. If `poolId` is provided, subsequently delegates the newly
|
||||||
|
* staked ZRX with that pool.
|
||||||
|
*/
|
||||||
|
public async stakeAsync(amount: BigNumber, poolId?: string): Promise<void> {
|
||||||
|
const { stakingWrapper } = this.actor.deployment.staking;
|
||||||
|
await stakingWrapper.stake.awaitTransactionSuccessAsync(amount, {
|
||||||
|
from: this.actor.address,
|
||||||
|
});
|
||||||
|
if (poolId !== undefined) {
|
||||||
|
await stakingWrapper.moveStake.awaitTransactionSuccessAsync(
|
||||||
|
new StakeInfo(StakeStatus.Undelegated),
|
||||||
|
new StakeInfo(StakeStatus.Delegated, poolId),
|
||||||
|
amount,
|
||||||
|
{ from: this.actor.address },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Staker extends StakerMixin(Actor) {}
|
45
contracts/integrations/test/actors/taker.ts
Normal file
45
contracts/integrations/test/actors/taker.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import { SignedOrder } from '@0x/types';
|
||||||
|
import { BigNumber } from '@0x/utils';
|
||||||
|
import { TransactionReceiptWithDecodedLogs, TxData } from 'ethereum-types';
|
||||||
|
|
||||||
|
import { Actor, Constructor } from './base';
|
||||||
|
import { DeploymentManager } from '../utils/deployment_manager';
|
||||||
|
|
||||||
|
export function TakerMixin<TBase extends Constructor>(Base: TBase) {
|
||||||
|
return class extends Base {
|
||||||
|
public readonly actor: Actor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The mixin pattern requires that this constructor uses `...args: any[]`, but this class
|
||||||
|
* really expects a single `ActorConfig` parameter (assuming `Actor` is used as the base
|
||||||
|
* class).
|
||||||
|
*/
|
||||||
|
constructor(...args: any[]) {
|
||||||
|
super(...args);
|
||||||
|
this.actor = (this as any) as Actor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fills an order by the given `fillAmount`. Defaults to paying the protocol fee in ETH.
|
||||||
|
*/
|
||||||
|
public async fillOrderAsync(
|
||||||
|
order: SignedOrder,
|
||||||
|
fillAmount: BigNumber,
|
||||||
|
txData: Partial<TxData> = {},
|
||||||
|
): Promise<TransactionReceiptWithDecodedLogs> {
|
||||||
|
return this.actor.deployment.exchange.fillOrder.awaitTransactionSuccessAsync(
|
||||||
|
order,
|
||||||
|
fillAmount,
|
||||||
|
order.signature,
|
||||||
|
{
|
||||||
|
from: this.actor.address,
|
||||||
|
gasPrice: DeploymentManager.gasPrice,
|
||||||
|
value: DeploymentManager.protocolFee,
|
||||||
|
...txData,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Taker extends TakerMixin(Actor) {}
|
@ -1,135 +0,0 @@
|
|||||||
import { blockchainTests, constants, expect, filterLogsToArguments, OrderFactory } from '@0x/contracts-test-utils';
|
|
||||||
import { DummyERC20TokenContract, IERC20TokenEvents, IERC20TokenTransferEventArgs } from '@0x/contracts-erc20';
|
|
||||||
import { IExchangeEvents, IExchangeFillEventArgs } from '@0x/contracts-exchange';
|
|
||||||
import { IStakingEventsEvents } from '@0x/contracts-staking';
|
|
||||||
import { assetDataUtils, orderHashUtils } from '@0x/order-utils';
|
|
||||||
import { BigNumber } from '@0x/utils';
|
|
||||||
|
|
||||||
import { AddressManager } from '../utils/address_manager';
|
|
||||||
import { DeploymentManager } from '../utils/deployment_manager';
|
|
||||||
|
|
||||||
blockchainTests('Exchange & Staking', env => {
|
|
||||||
let accounts: string[];
|
|
||||||
let makerAddress: string;
|
|
||||||
let takers: string[] = [];
|
|
||||||
let delegators: string[] = [];
|
|
||||||
let feeRecipientAddress: string;
|
|
||||||
let addressManager: AddressManager;
|
|
||||||
let deploymentManager: DeploymentManager;
|
|
||||||
let orderFactory: OrderFactory;
|
|
||||||
let makerAsset: DummyERC20TokenContract;
|
|
||||||
let takerAsset: DummyERC20TokenContract;
|
|
||||||
let feeAsset: DummyERC20TokenContract;
|
|
||||||
|
|
||||||
const GAS_PRICE = 1e9;
|
|
||||||
|
|
||||||
before(async () => {
|
|
||||||
const chainId = await env.getChainIdAsync();
|
|
||||||
accounts = await env.getAccountAddressesAsync();
|
|
||||||
[makerAddress, feeRecipientAddress, takers[0], takers[1], ...delegators] = accounts.slice(1);
|
|
||||||
deploymentManager = await DeploymentManager.deployAsync(env);
|
|
||||||
|
|
||||||
// Create a staking pool with the operator as a maker address.
|
|
||||||
await deploymentManager.staking.stakingWrapper.createStakingPool.awaitTransactionSuccessAsync(
|
|
||||||
constants.ZERO_AMOUNT,
|
|
||||||
true,
|
|
||||||
{ from: makerAddress },
|
|
||||||
);
|
|
||||||
|
|
||||||
// Set up an address for market making.
|
|
||||||
addressManager = new AddressManager();
|
|
||||||
await addressManager.addMakerAsync(
|
|
||||||
deploymentManager,
|
|
||||||
{
|
|
||||||
address: makerAddress,
|
|
||||||
mainToken: deploymentManager.tokens.erc20[0],
|
|
||||||
feeToken: deploymentManager.tokens.erc20[2],
|
|
||||||
},
|
|
||||||
env,
|
|
||||||
deploymentManager.tokens.erc20[1],
|
|
||||||
feeRecipientAddress,
|
|
||||||
chainId,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Set up two addresses for taking orders.
|
|
||||||
await Promise.all(
|
|
||||||
takers.map(taker =>
|
|
||||||
addressManager.addTakerAsync(deploymentManager, {
|
|
||||||
address: taker,
|
|
||||||
mainToken: deploymentManager.tokens.erc20[1],
|
|
||||||
feeToken: deploymentManager.tokens.erc20[2],
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('fillOrder', () => {
|
|
||||||
it('should be able to fill an order', async () => {
|
|
||||||
const order = await addressManager.makers[0].orderFactory.newSignedOrderAsync({
|
|
||||||
makerAddress,
|
|
||||||
makerAssetAmount: new BigNumber(1),
|
|
||||||
takerAssetAmount: new BigNumber(1),
|
|
||||||
makerFee: constants.ZERO_AMOUNT,
|
|
||||||
takerFee: constants.ZERO_AMOUNT,
|
|
||||||
feeRecipientAddress,
|
|
||||||
});
|
|
||||||
|
|
||||||
const receipt = await deploymentManager.exchange.fillOrder.awaitTransactionSuccessAsync(
|
|
||||||
order,
|
|
||||||
new BigNumber(1),
|
|
||||||
order.signature,
|
|
||||||
{
|
|
||||||
from: takers[0],
|
|
||||||
gasPrice: GAS_PRICE,
|
|
||||||
value: DeploymentManager.protocolFeeMultiplier.times(GAS_PRICE),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// Ensure that the number of emitted logs is equal to 3. There should have been a fill event
|
|
||||||
// and two transfer events. A 'StakingPoolActivated' event should not be expected because
|
|
||||||
// the only staking pool that was created does not have enough stake.
|
|
||||||
expect(receipt.logs.length).to.be.eq(3);
|
|
||||||
|
|
||||||
// Ensure that the fill event was correct.
|
|
||||||
const fillArgs = filterLogsToArguments<IExchangeFillEventArgs>(receipt.logs, IExchangeEvents.Fill);
|
|
||||||
expect(fillArgs.length).to.be.eq(1);
|
|
||||||
expect(fillArgs).to.be.deep.eq([
|
|
||||||
{
|
|
||||||
makerAddress,
|
|
||||||
feeRecipientAddress,
|
|
||||||
makerAssetData: order.makerAssetData,
|
|
||||||
takerAssetData: order.takerAssetData,
|
|
||||||
makerFeeAssetData: order.makerFeeAssetData,
|
|
||||||
takerFeeAssetData: order.takerFeeAssetData,
|
|
||||||
orderHash: orderHashUtils.getOrderHashHex(order),
|
|
||||||
takerAddress: takers[0],
|
|
||||||
senderAddress: takers[0],
|
|
||||||
makerAssetFilledAmount: order.makerAssetAmount,
|
|
||||||
takerAssetFilledAmount: order.takerAssetAmount,
|
|
||||||
makerFeePaid: constants.ZERO_AMOUNT,
|
|
||||||
takerFeePaid: constants.ZERO_AMOUNT,
|
|
||||||
protocolFeePaid: DeploymentManager.protocolFeeMultiplier.times(GAS_PRICE),
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Ensure that the transfer events were correctly emitted.
|
|
||||||
const transferArgs = filterLogsToArguments<IERC20TokenTransferEventArgs>(
|
|
||||||
receipt.logs,
|
|
||||||
IERC20TokenEvents.Transfer,
|
|
||||||
);
|
|
||||||
expect(transferArgs.length).to.be.eq(2);
|
|
||||||
expect(transferArgs).to.be.deep.eq([
|
|
||||||
{
|
|
||||||
_from: takers[0],
|
|
||||||
_to: makerAddress,
|
|
||||||
_value: order.takerAssetAmount,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
_from: makerAddress,
|
|
||||||
_to: takers[0],
|
|
||||||
_value: order.makerAssetAmount,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@ -0,0 +1,331 @@
|
|||||||
|
import { blockchainTests, constants, expect } from '@0x/contracts-test-utils';
|
||||||
|
import { IERC20TokenEvents, IERC20TokenTransferEventArgs } from '@0x/contracts-erc20';
|
||||||
|
import {
|
||||||
|
BlockchainBalanceStore,
|
||||||
|
IExchangeEvents,
|
||||||
|
IExchangeFillEventArgs,
|
||||||
|
LocalBalanceStore,
|
||||||
|
} from '@0x/contracts-exchange';
|
||||||
|
import {
|
||||||
|
constants as stakingConstants,
|
||||||
|
IStakingEventsEpochEndedEventArgs,
|
||||||
|
IStakingEventsEpochFinalizedEventArgs,
|
||||||
|
IStakingEventsEvents,
|
||||||
|
IStakingEventsRewardsPaidEventArgs,
|
||||||
|
IStakingEventsStakingPoolEarnedRewardsInEpochEventArgs,
|
||||||
|
} from '@0x/contracts-staking';
|
||||||
|
import { SignedOrder } from '@0x/types';
|
||||||
|
import { assetDataUtils, orderHashUtils } from '@0x/order-utils';
|
||||||
|
import { toBaseUnitAmount, verifyEvents } from '@0x/contracts-test-utils';
|
||||||
|
import { BigNumber } from '@0x/utils';
|
||||||
|
import { TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
||||||
|
|
||||||
|
import { Taker, actorAddressesByName, FeeRecipient, Maker, OperatorStakerMaker, StakerKeeper } from '../actors';
|
||||||
|
import { DeploymentManager } from '../utils/deployment_manager';
|
||||||
|
|
||||||
|
blockchainTests.resets('fillOrder integration tests', env => {
|
||||||
|
let deployment: DeploymentManager;
|
||||||
|
let balanceStore: BlockchainBalanceStore;
|
||||||
|
|
||||||
|
let feeRecipient: FeeRecipient;
|
||||||
|
let operator: OperatorStakerMaker;
|
||||||
|
let maker: Maker;
|
||||||
|
let taker: Taker;
|
||||||
|
let delegator: StakerKeeper;
|
||||||
|
|
||||||
|
let poolId: string;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
deployment = await DeploymentManager.deployAsync(env, {
|
||||||
|
numErc20TokensToDeploy: 2,
|
||||||
|
numErc721TokensToDeploy: 0,
|
||||||
|
numErc1155TokensToDeploy: 0,
|
||||||
|
});
|
||||||
|
const [makerToken, takerToken] = deployment.tokens.erc20;
|
||||||
|
|
||||||
|
feeRecipient = new FeeRecipient({
|
||||||
|
name: 'Fee recipient',
|
||||||
|
deployment,
|
||||||
|
});
|
||||||
|
const orderConfig = {
|
||||||
|
feeRecipientAddress: feeRecipient.address,
|
||||||
|
makerAssetData: assetDataUtils.encodeERC20AssetData(makerToken.address),
|
||||||
|
takerAssetData: assetDataUtils.encodeERC20AssetData(takerToken.address),
|
||||||
|
makerFeeAssetData: assetDataUtils.encodeERC20AssetData(makerToken.address),
|
||||||
|
takerFeeAssetData: assetDataUtils.encodeERC20AssetData(takerToken.address),
|
||||||
|
makerFee: constants.ZERO_AMOUNT,
|
||||||
|
takerFee: constants.ZERO_AMOUNT,
|
||||||
|
};
|
||||||
|
operator = new OperatorStakerMaker({
|
||||||
|
name: 'Pool operator',
|
||||||
|
deployment,
|
||||||
|
orderConfig,
|
||||||
|
});
|
||||||
|
maker = new Maker({
|
||||||
|
name: 'Maker',
|
||||||
|
deployment,
|
||||||
|
orderConfig,
|
||||||
|
});
|
||||||
|
taker = new Taker({ name: 'Taker', deployment });
|
||||||
|
delegator = new StakerKeeper({ name: 'Delegator', deployment });
|
||||||
|
|
||||||
|
await operator.configureERC20TokenAsync(makerToken);
|
||||||
|
await maker.configureERC20TokenAsync(makerToken);
|
||||||
|
await taker.configureERC20TokenAsync(takerToken);
|
||||||
|
await taker.configureERC20TokenAsync(deployment.tokens.weth, deployment.staking.stakingProxy.address);
|
||||||
|
|
||||||
|
await operator.configureERC20TokenAsync(deployment.tokens.zrx);
|
||||||
|
await delegator.configureERC20TokenAsync(deployment.tokens.zrx);
|
||||||
|
|
||||||
|
// Create a staking pool with the operator as a maker.
|
||||||
|
poolId = await operator.createStakingPoolAsync(0.95 * stakingConstants.PPM, true);
|
||||||
|
// A vanilla maker joins the pool as well.
|
||||||
|
await maker.joinStakingPoolAsync(poolId);
|
||||||
|
|
||||||
|
const tokenOwners = {
|
||||||
|
...actorAddressesByName([feeRecipient, operator, maker, taker, delegator]),
|
||||||
|
StakingProxy: deployment.staking.stakingProxy.address,
|
||||||
|
ZrxVault: deployment.staking.zrxVault.address,
|
||||||
|
};
|
||||||
|
console.log(tokenOwners);
|
||||||
|
const tokenContracts = {
|
||||||
|
erc20: { makerToken, takerToken, ZRX: deployment.tokens.zrx, WETH: deployment.tokens.weth },
|
||||||
|
};
|
||||||
|
balanceStore = new BlockchainBalanceStore(tokenOwners, tokenContracts);
|
||||||
|
await balanceStore.updateBalancesAsync();
|
||||||
|
});
|
||||||
|
|
||||||
|
function simulateFill(
|
||||||
|
order: SignedOrder,
|
||||||
|
txReceipt: TransactionReceiptWithDecodedLogs,
|
||||||
|
msgValue: BigNumber = DeploymentManager.protocolFee,
|
||||||
|
): LocalBalanceStore {
|
||||||
|
const localBalanceStore = LocalBalanceStore.create(balanceStore);
|
||||||
|
// Transaction gas cost
|
||||||
|
localBalanceStore.burnGas(txReceipt.from, DeploymentManager.gasPrice.times(txReceipt.gasUsed));
|
||||||
|
|
||||||
|
// Taker -> Maker
|
||||||
|
localBalanceStore.transferAsset(taker.address, maker.address, order.takerAssetAmount, order.takerAssetData);
|
||||||
|
// Maker -> Taker
|
||||||
|
localBalanceStore.transferAsset(maker.address, taker.address, order.makerAssetAmount, order.makerAssetData);
|
||||||
|
|
||||||
|
// Protocol fee
|
||||||
|
if (msgValue.isGreaterThanOrEqualTo(DeploymentManager.protocolFee)) {
|
||||||
|
localBalanceStore.sendEth(
|
||||||
|
txReceipt.from,
|
||||||
|
deployment.staking.stakingProxy.address,
|
||||||
|
DeploymentManager.protocolFee,
|
||||||
|
);
|
||||||
|
msgValue = msgValue.minus(DeploymentManager.protocolFee);
|
||||||
|
} else {
|
||||||
|
localBalanceStore.transferAsset(
|
||||||
|
taker.address,
|
||||||
|
deployment.staking.stakingProxy.address,
|
||||||
|
DeploymentManager.protocolFee,
|
||||||
|
assetDataUtils.encodeERC20AssetData(deployment.tokens.weth.address),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return localBalanceStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
function verifyFillEvents(order: SignedOrder, receipt: TransactionReceiptWithDecodedLogs): void {
|
||||||
|
// Ensure that the fill event was correct.
|
||||||
|
verifyEvents<IExchangeFillEventArgs>(
|
||||||
|
receipt,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
makerAddress: maker.address,
|
||||||
|
feeRecipientAddress: feeRecipient.address,
|
||||||
|
makerAssetData: order.makerAssetData,
|
||||||
|
takerAssetData: order.takerAssetData,
|
||||||
|
makerFeeAssetData: order.makerFeeAssetData,
|
||||||
|
takerFeeAssetData: order.takerFeeAssetData,
|
||||||
|
orderHash: orderHashUtils.getOrderHashHex(order),
|
||||||
|
takerAddress: taker.address,
|
||||||
|
senderAddress: taker.address,
|
||||||
|
makerAssetFilledAmount: order.makerAssetAmount,
|
||||||
|
takerAssetFilledAmount: order.takerAssetAmount,
|
||||||
|
makerFeePaid: constants.ZERO_AMOUNT,
|
||||||
|
takerFeePaid: constants.ZERO_AMOUNT,
|
||||||
|
protocolFeePaid: DeploymentManager.protocolFee,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
IExchangeEvents.Fill,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Ensure that the transfer events were correctly emitted.
|
||||||
|
verifyEvents<IERC20TokenTransferEventArgs>(
|
||||||
|
receipt,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
_from: taker.address,
|
||||||
|
_to: maker.address,
|
||||||
|
_value: order.takerAssetAmount,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
_from: maker.address,
|
||||||
|
_to: taker.address,
|
||||||
|
_value: order.makerAssetAmount,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
IERC20TokenEvents.Transfer,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
it('should fill an order', async () => {
|
||||||
|
// Create and fill the order
|
||||||
|
const order = await maker.signOrderAsync();
|
||||||
|
const receipt = await taker.fillOrderAsync(order, order.takerAssetAmount);
|
||||||
|
|
||||||
|
// Check balances
|
||||||
|
const expectedBalances = simulateFill(order, receipt);
|
||||||
|
await balanceStore.updateBalancesAsync();
|
||||||
|
balanceStore.assertEquals(expectedBalances);
|
||||||
|
|
||||||
|
// There should have been a fill event and two transfer events. A
|
||||||
|
// 'StakingPoolEarnedRewardsInEpoch' event should not be expected because the only staking
|
||||||
|
// pool that was created does not have enough stake.
|
||||||
|
verifyFillEvents(order, receipt);
|
||||||
|
});
|
||||||
|
it('should activate a staking pool if it has sufficient stake', async () => {
|
||||||
|
// Stake just enough to qualify the pool for rewards.
|
||||||
|
await delegator.stakeAsync(toBaseUnitAmount(100), poolId);
|
||||||
|
|
||||||
|
// The delegator, functioning as a keeper, ends the epoch so that delegated stake (theirs
|
||||||
|
// and the operator's) becomes active. This puts the staking pool above the minimumPoolStake
|
||||||
|
// threshold, so it should be able to earn rewards in the new epoch.
|
||||||
|
// Finalizing the pool shouldn't settle rewards because it didn't earn rewards last epoch.
|
||||||
|
await delegator.endEpochAsync();
|
||||||
|
await delegator.finalizePoolsAsync([poolId]);
|
||||||
|
await balanceStore.updateBalancesAsync();
|
||||||
|
|
||||||
|
// Create and fill the order
|
||||||
|
const order = await maker.signOrderAsync();
|
||||||
|
const receipt = await taker.fillOrderAsync(order, order.takerAssetAmount);
|
||||||
|
|
||||||
|
// Check balances
|
||||||
|
const expectedBalances = simulateFill(order, receipt);
|
||||||
|
await balanceStore.updateBalancesAsync();
|
||||||
|
balanceStore.assertEquals(expectedBalances);
|
||||||
|
|
||||||
|
// In addition to the fill event and two transfer events emitted in the previous test, we
|
||||||
|
// now expect a `StakingPoolEarnedRewardsInEpoch` event to be emitted because the staking
|
||||||
|
// pool now has enough stake in the current epoch to earn rewards.
|
||||||
|
verifyFillEvents(order, receipt);
|
||||||
|
const currentEpoch = await deployment.staking.stakingWrapper.currentEpoch.callAsync();
|
||||||
|
verifyEvents<IStakingEventsStakingPoolEarnedRewardsInEpochEventArgs>(
|
||||||
|
receipt,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
epoch: currentEpoch,
|
||||||
|
poolId,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
IStakingEventsEvents.StakingPoolEarnedRewardsInEpoch,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('should pay out rewards to operator and delegator', async () => {
|
||||||
|
// Operator and delegator each stake some ZRX; wait an epoch so that the stake is active.
|
||||||
|
await operator.stakeAsync(toBaseUnitAmount(100), poolId);
|
||||||
|
await delegator.stakeAsync(toBaseUnitAmount(50), poolId);
|
||||||
|
await delegator.endEpochAsync();
|
||||||
|
|
||||||
|
// Create and fill the order. One order's worth of protocol fees are now available as rewards.
|
||||||
|
const order = await maker.signOrderAsync();
|
||||||
|
await taker.fillOrderAsync(order, order.takerAssetAmount);
|
||||||
|
const rewardsAvailable = DeploymentManager.protocolFee;
|
||||||
|
|
||||||
|
// Fetch the current balances
|
||||||
|
await balanceStore.updateBalancesAsync();
|
||||||
|
const expectedBalances = LocalBalanceStore.create(balanceStore);
|
||||||
|
|
||||||
|
// End the epoch. This should wrap the staking proxy's ETH balance.
|
||||||
|
const endEpochReceipt = await delegator.endEpochAsync();
|
||||||
|
const newEpoch = await deployment.staking.stakingWrapper.currentEpoch.callAsync();
|
||||||
|
|
||||||
|
// Check balances
|
||||||
|
expectedBalances.wrapEth(
|
||||||
|
deployment.staking.stakingProxy.address,
|
||||||
|
deployment.tokens.weth.address,
|
||||||
|
DeploymentManager.protocolFee,
|
||||||
|
);
|
||||||
|
expectedBalances.burnGas(delegator.address, DeploymentManager.gasPrice.times(endEpochReceipt.gasUsed));
|
||||||
|
await balanceStore.updateBalancesAsync();
|
||||||
|
balanceStore.assertEquals(expectedBalances);
|
||||||
|
|
||||||
|
// Check the EpochEnded event
|
||||||
|
const weightedDelegatorStake = toBaseUnitAmount(50).times(0.9);
|
||||||
|
verifyEvents<IStakingEventsEpochEndedEventArgs>(
|
||||||
|
endEpochReceipt,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
epoch: newEpoch.minus(1),
|
||||||
|
numPoolsToFinalize: new BigNumber(1),
|
||||||
|
rewardsAvailable,
|
||||||
|
totalFeesCollected: DeploymentManager.protocolFee,
|
||||||
|
totalWeightedStake: toBaseUnitAmount(100).plus(weightedDelegatorStake),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
IStakingEventsEvents.EpochEnded,
|
||||||
|
);
|
||||||
|
|
||||||
|
// The rewards are split between the operator and delegator based on the pool's operatorShare
|
||||||
|
const operatorReward = rewardsAvailable
|
||||||
|
.times(operator.operatorShares[poolId])
|
||||||
|
.dividedToIntegerBy(constants.PPM_DENOMINATOR);
|
||||||
|
const delegatorReward = rewardsAvailable.minus(operatorReward);
|
||||||
|
|
||||||
|
// Finalize the pool. This should automatically pay the operator in WETH.
|
||||||
|
const [finalizePoolReceipt] = await delegator.finalizePoolsAsync([poolId]);
|
||||||
|
|
||||||
|
// Check balances
|
||||||
|
expectedBalances.transferAsset(
|
||||||
|
deployment.staking.stakingProxy.address,
|
||||||
|
operator.address,
|
||||||
|
operatorReward,
|
||||||
|
assetDataUtils.encodeERC20AssetData(deployment.tokens.weth.address),
|
||||||
|
);
|
||||||
|
expectedBalances.burnGas(delegator.address, DeploymentManager.gasPrice.times(finalizePoolReceipt.gasUsed));
|
||||||
|
await balanceStore.updateBalancesAsync();
|
||||||
|
balanceStore.assertEquals(expectedBalances);
|
||||||
|
|
||||||
|
// Check finalization events
|
||||||
|
verifyEvents<IStakingEventsRewardsPaidEventArgs>(
|
||||||
|
finalizePoolReceipt,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
epoch: newEpoch,
|
||||||
|
poolId,
|
||||||
|
operatorReward,
|
||||||
|
membersReward: delegatorReward,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
IStakingEventsEvents.RewardsPaid,
|
||||||
|
);
|
||||||
|
verifyEvents<IStakingEventsEpochFinalizedEventArgs>(
|
||||||
|
finalizePoolReceipt,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
epoch: newEpoch.minus(1),
|
||||||
|
rewardsPaid: rewardsAvailable,
|
||||||
|
rewardsRemaining: constants.ZERO_AMOUNT,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
IStakingEventsEvents.EpochFinalized,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('should credit rewards from orders made by the operator to their pool', async () => {
|
||||||
|
// Stake just enough to qualify the pool for rewards.
|
||||||
|
await delegator.stakeAsync(toBaseUnitAmount(100), poolId);
|
||||||
|
await delegator.endEpochAsync();
|
||||||
|
|
||||||
|
// Create and fill the order
|
||||||
|
const order = await operator.signOrderAsync();
|
||||||
|
await taker.fillOrderAsync(order, order.takerAssetAmount);
|
||||||
|
|
||||||
|
// Check that the pool has collected fees from the above fill.
|
||||||
|
const poolStats = await deployment.staking.stakingWrapper.getStakingPoolStatsThisEpoch.callAsync(poolId);
|
||||||
|
expect(poolStats.feesCollected).to.bignumber.equal(DeploymentManager.protocolFee);
|
||||||
|
});
|
||||||
|
});
|
@ -1,97 +0,0 @@
|
|||||||
import { DummyERC20TokenContract } from '@0x/contracts-erc20';
|
|
||||||
import { constants, OrderFactory, BlockchainTestsEnvironment } from '@0x/contracts-test-utils';
|
|
||||||
import { assetDataUtils, Order, SignatureType, SignedOrder } from '@0x/order-utils';
|
|
||||||
|
|
||||||
import { DeploymentManager } from '../../src';
|
|
||||||
|
|
||||||
interface MarketMaker {
|
|
||||||
address: string;
|
|
||||||
orderFactory: OrderFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ConfigurationArgs {
|
|
||||||
address: string;
|
|
||||||
mainToken: DummyERC20TokenContract;
|
|
||||||
feeToken: DummyERC20TokenContract;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class AddressManager {
|
|
||||||
// A set of addresses that have been configured for market making.
|
|
||||||
public makers: MarketMaker[];
|
|
||||||
|
|
||||||
// A set of addresses that have been configured to take orders.
|
|
||||||
public takers: string[];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets up an address to take orders.
|
|
||||||
*/
|
|
||||||
public async addTakerAsync(deploymentManager: DeploymentManager, configArgs: ConfigurationArgs): Promise<void> {
|
|
||||||
// Configure the taker address with the taker and fee tokens.
|
|
||||||
await this._configureTokenForAddressAsync(deploymentManager, configArgs.address, configArgs.mainToken);
|
|
||||||
await this._configureTokenForAddressAsync(deploymentManager, configArgs.address, configArgs.feeToken);
|
|
||||||
|
|
||||||
// Add the taker to the list of configured taker addresses.
|
|
||||||
this.takers.push(configArgs.address);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets up an address for market making.
|
|
||||||
*/
|
|
||||||
public async addMakerAsync(
|
|
||||||
deploymentManager: DeploymentManager,
|
|
||||||
configArgs: ConfigurationArgs,
|
|
||||||
environment: BlockchainTestsEnvironment,
|
|
||||||
takerToken: DummyERC20TokenContract,
|
|
||||||
feeRecipientAddress: string,
|
|
||||||
chainId: number,
|
|
||||||
): Promise<void> {
|
|
||||||
const accounts = await environment.getAccountAddressesAsync();
|
|
||||||
|
|
||||||
// Set up order signing for the maker address.
|
|
||||||
const defaultOrderParams = {
|
|
||||||
...constants.STATIC_ORDER_PARAMS,
|
|
||||||
makerAddress: configArgs.address,
|
|
||||||
makerAssetData: assetDataUtils.encodeERC20AssetData(configArgs.mainToken.address),
|
|
||||||
takerAssetData: assetDataUtils.encodeERC20AssetData(takerToken.address),
|
|
||||||
makerFeeAssetData: assetDataUtils.encodeERC20AssetData(configArgs.feeToken.address),
|
|
||||||
takerFeeAssetData: assetDataUtils.encodeERC20AssetData(configArgs.feeToken.address),
|
|
||||||
feeRecipientAddress,
|
|
||||||
exchangeAddress: deploymentManager.exchange.address,
|
|
||||||
chainId,
|
|
||||||
};
|
|
||||||
const privateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(configArgs.address)];
|
|
||||||
const orderFactory = new OrderFactory(privateKey, defaultOrderParams);
|
|
||||||
|
|
||||||
// Configure the maker address with the maker and fee tokens.
|
|
||||||
await this._configureTokenForAddressAsync(deploymentManager, configArgs.address, configArgs.mainToken);
|
|
||||||
await this._configureTokenForAddressAsync(deploymentManager, configArgs.address, configArgs.feeToken);
|
|
||||||
|
|
||||||
// Add the maker to the list of configured maker addresses.
|
|
||||||
this.makers.push({
|
|
||||||
address: configArgs.address,
|
|
||||||
orderFactory,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets up initial account balances for a token and approves the ERC20 asset proxy
|
|
||||||
* to transfer the token.
|
|
||||||
*/
|
|
||||||
protected async _configureTokenForAddressAsync(
|
|
||||||
deploymentManager: DeploymentManager,
|
|
||||||
address: string,
|
|
||||||
token: DummyERC20TokenContract,
|
|
||||||
): Promise<void> {
|
|
||||||
await token.setBalance.awaitTransactionSuccessAsync(address, constants.INITIAL_ERC20_BALANCE);
|
|
||||||
await token.approve.awaitTransactionSuccessAsync(
|
|
||||||
deploymentManager.assetProxies.erc20Proxy.address,
|
|
||||||
constants.MAX_UINT256,
|
|
||||||
{ from: address },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this.makers = [];
|
|
||||||
this.takers = [];
|
|
||||||
}
|
|
||||||
}
|
|
@ -7,12 +7,7 @@ import {
|
|||||||
StaticCallProxyContract,
|
StaticCallProxyContract,
|
||||||
} from '@0x/contracts-asset-proxy';
|
} from '@0x/contracts-asset-proxy';
|
||||||
import { artifacts as ERC1155Artifacts, ERC1155MintableContract } from '@0x/contracts-erc1155';
|
import { artifacts as ERC1155Artifacts, ERC1155MintableContract } from '@0x/contracts-erc1155';
|
||||||
import {
|
import { DummyERC20TokenContract, artifacts as ERC20Artifacts, WETH9Contract } from '@0x/contracts-erc20';
|
||||||
DummyERC20TokenContract,
|
|
||||||
artifacts as ERC20Artifacts,
|
|
||||||
ZRXTokenContract,
|
|
||||||
WETH9Contract,
|
|
||||||
} from '@0x/contracts-erc20';
|
|
||||||
import { artifacts as ERC721Artifacts, DummyERC721TokenContract } from '@0x/contracts-erc721';
|
import { artifacts as ERC721Artifacts, DummyERC721TokenContract } from '@0x/contracts-erc721';
|
||||||
import {
|
import {
|
||||||
artifacts as exchangeArtifacts,
|
artifacts as exchangeArtifacts,
|
||||||
@ -108,7 +103,7 @@ interface TokenContracts {
|
|||||||
erc721: DummyERC721TokenContract[];
|
erc721: DummyERC721TokenContract[];
|
||||||
erc1155: ERC1155MintableContract[];
|
erc1155: ERC1155MintableContract[];
|
||||||
weth: WETH9Contract;
|
weth: WETH9Contract;
|
||||||
zrx: ZRXTokenContract;
|
zrx: DummyERC20TokenContract;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Options to be passed to `deployAsync`
|
// Options to be passed to `deployAsync`
|
||||||
@ -150,7 +145,7 @@ export class DeploymentManager {
|
|||||||
exchangeArtifacts.Exchange,
|
exchangeArtifacts.Exchange,
|
||||||
environment.provider,
|
environment.provider,
|
||||||
txDefaults,
|
txDefaults,
|
||||||
{ ...ERC20Artifacts, ...exchangeArtifacts },
|
{ ...ERC20Artifacts, ...exchangeArtifacts, ...stakingArtifacts },
|
||||||
new BigNumber(chainId),
|
new BigNumber(chainId),
|
||||||
);
|
);
|
||||||
const governor = await ZeroExGovernorContract.deployFrom0xArtifactAsync(
|
const governor = await ZeroExGovernorContract.deployFrom0xArtifactAsync(
|
||||||
@ -357,7 +352,7 @@ export class DeploymentManager {
|
|||||||
txDefaults,
|
txDefaults,
|
||||||
stakingArtifacts,
|
stakingArtifacts,
|
||||||
tokens.weth.address,
|
tokens.weth.address,
|
||||||
tokens.zrx.address,
|
zrxVault.address,
|
||||||
);
|
);
|
||||||
const stakingProxy = await StakingProxyContract.deployFrom0xArtifactAsync(
|
const stakingProxy = await StakingProxyContract.deployFrom0xArtifactAsync(
|
||||||
stakingArtifacts.StakingProxy,
|
stakingArtifacts.StakingProxy,
|
||||||
@ -366,7 +361,17 @@ export class DeploymentManager {
|
|||||||
stakingArtifacts,
|
stakingArtifacts,
|
||||||
stakingLogic.address,
|
stakingLogic.address,
|
||||||
);
|
);
|
||||||
const stakingWrapper = new TestStakingContract(stakingProxy.address, environment.provider);
|
|
||||||
|
const logDecoderDependencies = _.mapValues(
|
||||||
|
{ ...stakingArtifacts, ...ERC20Artifacts, ...exchangeArtifacts },
|
||||||
|
v => v.compilerOutput.abi,
|
||||||
|
);
|
||||||
|
const stakingWrapper = new TestStakingContract(
|
||||||
|
stakingProxy.address,
|
||||||
|
environment.provider,
|
||||||
|
txDefaults,
|
||||||
|
logDecoderDependencies,
|
||||||
|
);
|
||||||
|
|
||||||
// Add the zrx vault and the weth contract to the staking proxy.
|
// Add the zrx vault and the weth contract to the staking proxy.
|
||||||
await stakingWrapper.setWethContract.awaitTransactionSuccessAsync(tokens.weth.address, { from: owner });
|
await stakingWrapper.setWethContract.awaitTransactionSuccessAsync(tokens.weth.address, { from: owner });
|
||||||
@ -376,9 +381,13 @@ export class DeploymentManager {
|
|||||||
await stakingProxy.addAuthorizedAddress.awaitTransactionSuccessAsync(owner, { from: owner });
|
await stakingProxy.addAuthorizedAddress.awaitTransactionSuccessAsync(owner, { from: owner });
|
||||||
await zrxVault.addAuthorizedAddress.awaitTransactionSuccessAsync(owner, { from: owner });
|
await zrxVault.addAuthorizedAddress.awaitTransactionSuccessAsync(owner, { from: owner });
|
||||||
|
|
||||||
|
// Authorize the zrx vault in the erc20 proxy
|
||||||
|
await assetProxies.erc20Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(zrxVault.address, {
|
||||||
|
from: owner,
|
||||||
|
});
|
||||||
|
|
||||||
// Configure the zrx vault and the staking contract.
|
// Configure the zrx vault and the staking contract.
|
||||||
await zrxVault.setStakingProxy.awaitTransactionSuccessAsync(stakingProxy.address, { from: owner });
|
await zrxVault.setStakingProxy.awaitTransactionSuccessAsync(stakingProxy.address, { from: owner });
|
||||||
await zrxVault.setStakingProxy.awaitTransactionSuccessAsync(stakingProxy.address, { from: owner });
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
stakingLogic,
|
stakingLogic,
|
||||||
@ -461,11 +470,15 @@ export class DeploymentManager {
|
|||||||
txDefaults,
|
txDefaults,
|
||||||
ERC20Artifacts,
|
ERC20Artifacts,
|
||||||
);
|
);
|
||||||
const zrx = await ZRXTokenContract.deployFrom0xArtifactAsync(
|
const zrx = await DummyERC20TokenContract.deployFrom0xArtifactAsync(
|
||||||
ERC20Artifacts.ZRXToken,
|
ERC20Artifacts.DummyERC20Token,
|
||||||
environment.provider,
|
environment.provider,
|
||||||
txDefaults,
|
txDefaults,
|
||||||
ERC20Artifacts,
|
ERC20Artifacts,
|
||||||
|
constants.DUMMY_TOKEN_NAME,
|
||||||
|
constants.DUMMY_TOKEN_SYMBOL,
|
||||||
|
constants.DUMMY_TOKEN_DECIMALS,
|
||||||
|
constants.DUMMY_TOKEN_TOTAL_SUPPLY,
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -82,7 +82,6 @@ contract MixinExchangeFees is
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Look up the pool stats and aggregated stats for this epoch.
|
// Look up the pool stats and aggregated stats for this epoch.
|
||||||
|
|
||||||
uint256 currentEpoch_ = currentEpoch;
|
uint256 currentEpoch_ = currentEpoch;
|
||||||
IStructs.PoolStats storage poolStatsPtr = poolStatsByEpoch[poolId][currentEpoch_];
|
IStructs.PoolStats storage poolStatsPtr = poolStatsByEpoch[poolId][currentEpoch_];
|
||||||
IStructs.AggregatedStats storage aggregatedStatsPtr = aggregatedStatsByEpoch[currentEpoch_];
|
IStructs.AggregatedStats storage aggregatedStatsPtr = aggregatedStatsByEpoch[currentEpoch_];
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
|
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
|
||||||
"abis": "./generated-artifacts/@(IStaking|IStakingEvents|IStakingProxy|IStorage|IStorageInit|IStructs|IZrxVault|LibCobbDouglas|LibFixedMath|LibFixedMathRichErrors|LibSafeDowncast|LibStakingRichErrors|MixinAbstract|MixinConstants|MixinCumulativeRewards|MixinDeploymentConstants|MixinExchangeFees|MixinExchangeManager|MixinFinalizer|MixinParams|MixinScheduler|MixinStake|MixinStakeBalances|MixinStakeStorage|MixinStakingPool|MixinStakingPoolRewards|MixinStorage|Staking|StakingProxy|TestAssertStorageParams|TestCobbDouglas|TestCumulativeRewardTracking|TestDelegatorRewards|TestExchangeManager|TestFinalizer|TestInitTarget|TestLibFixedMath|TestLibSafeDowncast|TestMixinParams|TestMixinStake|TestMixinStakeStorage|TestMixinStakingPool|TestProtocolFees|TestStaking|TestStakingNoWETH|TestStakingProxy|TestStorageLayoutAndConstants|ZrxVault).json"
|
"abis": "./generated-artifacts/@(IStaking|IStakingEvents|IStakingProxy|IStorage|IStorageInit|IStructs|IZrxVault|LibCobbDouglas|LibFixedMath|LibFixedMathRichErrors|LibSafeDowncast|LibStakingRichErrors|MixinAbstract|MixinConstants|MixinCumulativeRewards|MixinDeploymentConstants|MixinExchangeFees|MixinExchangeManager|MixinFinalizer|MixinParams|MixinScheduler|MixinStake|MixinStakeBalances|MixinStakeStorage|MixinStakingPool|MixinStakingPoolRewards|MixinStorage|Staking|StakingProxy|TestAssertStorageParams|TestCobbDouglas|TestCumulativeRewardTracking|TestDelegatorRewards|TestExchangeManager|TestFinalizer|TestInitTarget|TestLibFixedMath|TestLibSafeDowncast|TestMixinParams|TestMixinStake|TestMixinStakeBalances|TestMixinStakeStorage|TestMixinStakingPool|TestProtocolFees|TestStaking|TestStakingNoWETH|TestStakingProxy|TestStorageLayoutAndConstants|ZrxVault).json"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
export * from './wrappers';
|
export * from './wrappers';
|
||||||
export * from './artifacts';
|
export * from './artifacts';
|
||||||
export { constants } from '../test/utils/constants';
|
export { constants } from '../test/utils/constants';
|
||||||
|
export * from '../test/utils/types';
|
||||||
|
@ -38,8 +38,8 @@ export function verifyEventsFromLogs<TEventArgs>(
|
|||||||
eventName: string,
|
eventName: string,
|
||||||
): void {
|
): void {
|
||||||
const _logs = filterLogsToArguments<TEventArgs>(logs, eventName);
|
const _logs = filterLogsToArguments<TEventArgs>(logs, eventName);
|
||||||
expect(_logs.length).to.eq(expectedEvents.length);
|
expect(_logs.length, `Number of ${eventName} events emitted`).to.eq(expectedEvents.length);
|
||||||
_logs.forEach((log, index) => {
|
_logs.forEach((log, index) => {
|
||||||
expect(log).to.deep.equal(expectedEvents[index]);
|
expect(log, `${eventName} event ${index}`).to.deep.equal(expectedEvents[index]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user