268 lines
13 KiB
TypeScript
268 lines
13 KiB
TypeScript
import { ERC20ProxyContract, ERC20Wrapper } from '@0x/contracts-asset-proxy';
|
|
import { DummyERC20TokenContract } from '@0x/contracts-erc20';
|
|
import {
|
|
chaiSetup,
|
|
expectTransactionFailedAsync,
|
|
provider,
|
|
web3Wrapper,
|
|
} from '@0x/contracts-test-utils';
|
|
import { BlockchainLifecycle } from '@0x/dev-utils';
|
|
import { RevertReason } from '@0x/types';
|
|
import { BigNumber } from '@0x/utils';
|
|
import * as chai from 'chai';
|
|
import * as ethUtil from 'ethereumjs-util';
|
|
import * as _ from 'lodash';
|
|
|
|
import { MakerActor } from './actors/maker_actor';
|
|
import { PoolOperatorActor } from './actors/pool_operator_actor';
|
|
import { constants as stakingConstants } from './utils/constants';
|
|
import { StakingWrapper } from './utils/staking_wrapper';
|
|
|
|
chaiSetup.configure();
|
|
const expect = chai.expect;
|
|
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
|
// tslint:disable:no-unnecessary-type-assertion
|
|
describe('Staking Pool Management', () => {
|
|
// constants
|
|
const ZRX_TOKEN_DECIMALS = new BigNumber(18);
|
|
// tokens & addresses
|
|
let accounts: string[];
|
|
let owner: string;
|
|
let users: string[];
|
|
let zrxTokenContract: DummyERC20TokenContract;
|
|
let erc20ProxyContract: ERC20ProxyContract;
|
|
// wrappers
|
|
let stakingWrapper: StakingWrapper;
|
|
let erc20Wrapper: ERC20Wrapper;
|
|
// tests
|
|
before(async () => {
|
|
await blockchainLifecycle.startAsync();
|
|
});
|
|
after(async () => {
|
|
await blockchainLifecycle.revertAsync();
|
|
});
|
|
before(async () => {
|
|
// create accounts
|
|
accounts = await web3Wrapper.getAvailableAddressesAsync();
|
|
owner = accounts[0];
|
|
users = accounts.slice(1);
|
|
// deploy erc20 proxy
|
|
erc20Wrapper = new ERC20Wrapper(provider, accounts, owner);
|
|
erc20ProxyContract = await erc20Wrapper.deployProxyAsync();
|
|
// deploy zrx token
|
|
[zrxTokenContract] = await erc20Wrapper.deployDummyTokensAsync(1, ZRX_TOKEN_DECIMALS);
|
|
await erc20Wrapper.setBalancesAndAllowancesAsync();
|
|
// deploy staking contracts
|
|
stakingWrapper = new StakingWrapper(provider, owner, erc20ProxyContract, zrxTokenContract, accounts);
|
|
await stakingWrapper.deployAndConfigureContractsAsync();
|
|
});
|
|
beforeEach(async () => {
|
|
await blockchainLifecycle.startAsync();
|
|
});
|
|
afterEach(async () => {
|
|
await blockchainLifecycle.revertAsync();
|
|
});
|
|
describe('Staking Pool Management', () => {
|
|
it('Should successfully create a pool', async () => {
|
|
// test parameters
|
|
const operatorAddress = users[0];
|
|
const operatorShare = 39;
|
|
const poolOperator = new PoolOperatorActor(operatorAddress, stakingWrapper);
|
|
// create pool
|
|
const poolId = await poolOperator.createPoolAsync(operatorShare);
|
|
expect(poolId).to.be.equal(stakingConstants.INITIAL_POOL_ID);
|
|
// check that the next pool id was incremented
|
|
const expectedNextPoolId = '0x0000000000000000000000000000000200000000000000000000000000000000';
|
|
const nextPoolId = await stakingWrapper.getNextPoolIdAsync();
|
|
expect(nextPoolId).to.be.equal(expectedNextPoolId);
|
|
});
|
|
it('Should successfully add/remove a maker to a pool', async () => {
|
|
// test parameters
|
|
const operatorAddress = users[0];
|
|
const operatorShare = 39;
|
|
const poolOperator = new PoolOperatorActor(operatorAddress, stakingWrapper);
|
|
const makerAddress = users[1];
|
|
const maker = new MakerActor(makerAddress, stakingWrapper);
|
|
// create pool
|
|
const poolId = await poolOperator.createPoolAsync(operatorShare);
|
|
expect(poolId).to.be.equal(stakingConstants.INITIAL_POOL_ID);
|
|
// add maker to pool
|
|
const makerApproval = maker.signApprovalForStakingPool(poolId);
|
|
await poolOperator.addMakerToPoolAsync(poolId, makerAddress, makerApproval.signature);
|
|
// remove maker from pool
|
|
await poolOperator.removeMakerFromPoolAsync(poolId, makerAddress);
|
|
});
|
|
it('Should successfully add/remove multipler makers to the same pool', async () => {
|
|
// test parameters
|
|
const operatorAddress = users[0];
|
|
const operatorShare = 39;
|
|
const poolOperator = new PoolOperatorActor(operatorAddress, stakingWrapper);
|
|
const makerAddresses = users.slice(1, 4);
|
|
const makers = [
|
|
new MakerActor(makerAddresses[0], stakingWrapper),
|
|
new MakerActor(makerAddresses[1], stakingWrapper),
|
|
new MakerActor(makerAddresses[2], stakingWrapper),
|
|
];
|
|
// create pool
|
|
const poolId = await poolOperator.createPoolAsync(operatorShare);
|
|
expect(poolId).to.be.equal(stakingConstants.INITIAL_POOL_ID);
|
|
// add makers to pool
|
|
const makerApprovals = [
|
|
makers[0].signApprovalForStakingPool(poolId),
|
|
makers[1].signApprovalForStakingPool(poolId),
|
|
makers[2].signApprovalForStakingPool(poolId),
|
|
];
|
|
await poolOperator.addMakerToPoolAsync(poolId, makerAddresses[0], makerApprovals[0].signature);
|
|
await poolOperator.addMakerToPoolAsync(poolId, makerAddresses[1], makerApprovals[1].signature);
|
|
await poolOperator.addMakerToPoolAsync(poolId, makerAddresses[2], makerApprovals[2].signature);
|
|
// remove maker from pool
|
|
await poolOperator.removeMakerFromPoolAsync(poolId, makerAddresses[0]);
|
|
await poolOperator.removeMakerFromPoolAsync(poolId, makerAddresses[1]);
|
|
await poolOperator.removeMakerFromPoolAsync(poolId, makerAddresses[2]);
|
|
});
|
|
it('Should fail to add the same maker twice', async () => {
|
|
// test parameters
|
|
const operatorAddress = users[0];
|
|
const operatorShare = 39;
|
|
const poolOperator = new PoolOperatorActor(operatorAddress, stakingWrapper);
|
|
const makerAddress = users[1];
|
|
const maker = new MakerActor(makerAddress, stakingWrapper);
|
|
// create pool
|
|
const poolId = await poolOperator.createPoolAsync(operatorShare);
|
|
expect(poolId).to.be.equal(stakingConstants.INITIAL_POOL_ID);
|
|
// add maker to pool
|
|
const makerApproval = maker.signApprovalForStakingPool(poolId);
|
|
await poolOperator.addMakerToPoolAsync(poolId, makerAddress, makerApproval.signature);
|
|
// add same maker to pool again
|
|
await poolOperator.addMakerToPoolAsync(
|
|
poolId,
|
|
makerAddress,
|
|
makerApproval.signature,
|
|
RevertReason.MakerAddressAlreadyRegistered,
|
|
);
|
|
});
|
|
it('Should fail to remove a maker that does not exist', async () => {
|
|
// test parameters
|
|
const operatorAddress = users[0];
|
|
const operatorShare = 39;
|
|
const poolOperator = new PoolOperatorActor(operatorAddress, stakingWrapper);
|
|
const makerAddress = users[1];
|
|
// create pool
|
|
const poolId = await poolOperator.createPoolAsync(operatorShare);
|
|
expect(poolId).to.be.equal(stakingConstants.INITIAL_POOL_ID);
|
|
// remove non-existent maker from pool
|
|
await poolOperator.removeMakerFromPoolAsync(poolId, makerAddress, RevertReason.MakerAddressNotRegistered);
|
|
});
|
|
it('Should fail to add a maker who signed with the wrong private key', async () => {
|
|
// test parameters
|
|
const operatorAddress = users[0];
|
|
const operatorShare = 39;
|
|
const poolOperator = new PoolOperatorActor(operatorAddress, stakingWrapper);
|
|
const makerAddress = users[1];
|
|
const badMakerPrivateKey = ethUtil.toBuffer(
|
|
'0x0000000000000000000000000000000000000000000000000000000000000001',
|
|
);
|
|
const maker = new MakerActor(makerAddress, stakingWrapper, badMakerPrivateKey);
|
|
// create pool
|
|
const poolId = await poolOperator.createPoolAsync(operatorShare);
|
|
expect(poolId).to.be.equal(stakingConstants.INITIAL_POOL_ID);
|
|
// add maker to pool
|
|
const makerApproval = maker.signApprovalForStakingPool(poolId);
|
|
await poolOperator.addMakerToPoolAsync(
|
|
poolId,
|
|
makerAddress,
|
|
makerApproval.signature,
|
|
RevertReason.InvalidMakerSignature,
|
|
);
|
|
});
|
|
it('Should fail to add a maker who signed with the wrong staking contract address', async () => {
|
|
// test parameters
|
|
const operatorAddress = users[0];
|
|
const operatorShare = 39;
|
|
const poolOperator = new PoolOperatorActor(operatorAddress, stakingWrapper);
|
|
const makerAddress = users[1];
|
|
const forceMakerKeyLookup = undefined;
|
|
const notStakingContractAddress = users[2];
|
|
const maker = new MakerActor(makerAddress, stakingWrapper, forceMakerKeyLookup, notStakingContractAddress);
|
|
// create pool
|
|
const poolId = await poolOperator.createPoolAsync(operatorShare);
|
|
expect(poolId).to.be.equal(stakingConstants.INITIAL_POOL_ID);
|
|
// add maker to pool
|
|
const makerApproval = maker.signApprovalForStakingPool(poolId);
|
|
await poolOperator.addMakerToPoolAsync(
|
|
poolId,
|
|
makerAddress,
|
|
makerApproval.signature,
|
|
RevertReason.InvalidMakerSignature,
|
|
);
|
|
});
|
|
it('Should fail to add a maker who signed with the wrong chain id', async () => {
|
|
// test parameters
|
|
const operatorAddress = users[0];
|
|
const operatorShare = 39;
|
|
const poolOperator = new PoolOperatorActor(operatorAddress, stakingWrapper);
|
|
const makerAddress = users[1];
|
|
const forceMakerKeyLookup = undefined;
|
|
const forceStakingContractLookup = undefined;
|
|
const badChainId = 209348;
|
|
const maker = new MakerActor(
|
|
makerAddress,
|
|
stakingWrapper,
|
|
forceMakerKeyLookup,
|
|
forceStakingContractLookup,
|
|
badChainId,
|
|
);
|
|
// create pool
|
|
const poolId = await poolOperator.createPoolAsync(operatorShare);
|
|
expect(poolId).to.be.equal(stakingConstants.INITIAL_POOL_ID);
|
|
// add maker to pool
|
|
const makerApproval = maker.signApprovalForStakingPool(poolId);
|
|
await poolOperator.addMakerToPoolAsync(
|
|
poolId,
|
|
makerAddress,
|
|
makerApproval.signature,
|
|
RevertReason.InvalidMakerSignature,
|
|
);
|
|
});
|
|
it('Should fail to add a maker when called by someone other than the pool operator', async () => {
|
|
// test parameters
|
|
const operatorAddress = users[0];
|
|
const operatorShare = 39;
|
|
const poolOperator = new PoolOperatorActor(operatorAddress, stakingWrapper);
|
|
const makerAddress = users[1];
|
|
const maker = new MakerActor(makerAddress, stakingWrapper);
|
|
const notOperatorAddress = users[2];
|
|
// create pool
|
|
const poolId = await poolOperator.createPoolAsync(operatorShare);
|
|
expect(poolId).to.be.equal(stakingConstants.INITIAL_POOL_ID);
|
|
// add maker to pool
|
|
const makerApproval = maker.signApprovalForStakingPool(poolId);
|
|
await expectTransactionFailedAsync(
|
|
stakingWrapper.addMakerToPoolAsync(poolId, makerAddress, makerApproval.signature, notOperatorAddress),
|
|
RevertReason.OnlyCallableByPoolOperator,
|
|
);
|
|
});
|
|
it('Should fail to remove a maker when called by someone other than the pool operator', async () => {
|
|
// test parameters
|
|
const operatorAddress = users[0];
|
|
const operatorShare = 39;
|
|
const poolOperator = new PoolOperatorActor(operatorAddress, stakingWrapper);
|
|
const makerAddress = users[1];
|
|
const maker = new MakerActor(makerAddress, stakingWrapper);
|
|
const notOperatorAddress = users[2];
|
|
// create pool
|
|
const poolId = await poolOperator.createPoolAsync(operatorShare);
|
|
expect(poolId).to.be.equal(stakingConstants.INITIAL_POOL_ID);
|
|
// add maker to pool
|
|
const makerApproval = maker.signApprovalForStakingPool(poolId);
|
|
await poolOperator.addMakerToPoolAsync(poolId, makerAddress, makerApproval.signature);
|
|
// try to remove the maker address from an address other than the operator
|
|
await expectTransactionFailedAsync(
|
|
stakingWrapper.removeMakerFromPoolAsync(poolId, makerAddress, notOperatorAddress),
|
|
RevertReason.OnlyCallableByPoolOperatorOrMaker,
|
|
);
|
|
});
|
|
});
|
|
});
|
|
// tslint:enable:no-unnecessary-type-assertion
|