208 lines
8.8 KiB
TypeScript
208 lines
8.8 KiB
TypeScript
import { ECSignature, ZeroEx } from '0x.js';
|
|
import { BlockchainLifecycle, devConstants, web3Factory } from '@0xproject/dev-utils';
|
|
import { BigNumber } from '@0xproject/utils';
|
|
import { Web3Wrapper } from '@0xproject/web3-wrapper';
|
|
import * as chai from 'chai';
|
|
import ethUtil = require('ethereumjs-util');
|
|
import * as Web3 from 'web3';
|
|
|
|
import { Balances } from '../../util/balances';
|
|
import { constants } from '../../util/constants';
|
|
import { crypto } from '../../util/crypto';
|
|
import { ExchangeWrapper } from '../../util/exchange_wrapper';
|
|
import { Order } from '../../util/order';
|
|
import { OrderFactory } from '../../util/order_factory';
|
|
import { BalancesByOwner, ContractName, ExchangeContractErrs } from '../../util/types';
|
|
import { chaiSetup } from '../utils/chai_setup';
|
|
import { deployer } from '../utils/deployer';
|
|
|
|
chaiSetup.configure();
|
|
const expect = chai.expect;
|
|
const web3 = web3Factory.create();
|
|
const web3Wrapper = new Web3Wrapper(web3.currentProvider);
|
|
const blockchainLifecycle = new BlockchainLifecycle();
|
|
|
|
describe.only('Arbitrage', () => {
|
|
let coinbase: string;
|
|
let maker: string;
|
|
let edMaker: string;
|
|
let edFrontRunner: string;
|
|
const feeRecipient = ZeroEx.NULL_ADDRESS;
|
|
const INITIAL_BALANCE = ZeroEx.toBaseUnitAmount(new BigNumber(10000), 18);
|
|
const INITIAL_ALLOWANCE = ZeroEx.toBaseUnitAmount(new BigNumber(10000), 18);
|
|
|
|
let weth: Web3.ContractInstance;
|
|
let zrx: Web3.ContractInstance;
|
|
let arbitrage: Web3.ContractInstance;
|
|
let etherDelta: Web3.ContractInstance;
|
|
|
|
let order: Order;
|
|
let exWrapper: ExchangeWrapper;
|
|
let orderFactory: OrderFactory;
|
|
|
|
let zeroEx: ZeroEx;
|
|
|
|
before(async () => {
|
|
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
|
[coinbase, maker, edMaker, edFrontRunner] = accounts;
|
|
weth = await deployer.deployAsync(ContractName.DummyToken);
|
|
zrx = await deployer.deployAsync(ContractName.DummyToken);
|
|
const accountLevels = await deployer.deployAsync(ContractName.AccountLevels);
|
|
const edAdminAddress = accounts[0];
|
|
const edMakerFee = 0;
|
|
const edTakerFee = 0;
|
|
const edFeeRebate = 0;
|
|
etherDelta = await deployer.deployAsync(ContractName.EtherDelta, [
|
|
edAdminAddress,
|
|
feeRecipient,
|
|
accountLevels.address,
|
|
edMakerFee,
|
|
edTakerFee,
|
|
edFeeRebate,
|
|
]);
|
|
const tokenTransferProxy = await deployer.deployAsync(ContractName.TokenTransferProxy);
|
|
const exchange = await deployer.deployAsync(ContractName.Exchange, [zrx.address, tokenTransferProxy.address]);
|
|
await tokenTransferProxy.addAuthorizedAddress(exchange.address, { from: accounts[0] });
|
|
zeroEx = new ZeroEx(web3.currentProvider, {
|
|
exchangeContractAddress: exchange.address,
|
|
networkId: constants.TESTRPC_NETWORK_ID,
|
|
});
|
|
exWrapper = new ExchangeWrapper(exchange, zeroEx);
|
|
|
|
const defaultOrderParams = {
|
|
exchangeContractAddress: exchange.address,
|
|
maker,
|
|
feeRecipient,
|
|
makerToken: zrx.address,
|
|
takerToken: weth.address,
|
|
makerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(1), 18),
|
|
takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(1), 18),
|
|
makerFee: new BigNumber(0),
|
|
takerFee: new BigNumber(0),
|
|
};
|
|
orderFactory = new OrderFactory(web3Wrapper, defaultOrderParams);
|
|
arbitrage = await deployer.deployAsync(ContractName.Arbitrage, [
|
|
exchange.address,
|
|
etherDelta.address,
|
|
tokenTransferProxy.address,
|
|
]);
|
|
// Enable arbitrage and withdrawals of tokens
|
|
await arbitrage.setAllowances(weth.address, { from: coinbase });
|
|
await arbitrage.setAllowances(zrx.address, { from: coinbase });
|
|
|
|
// Give some tokens to arbitrage contract
|
|
await weth.setBalance(arbitrage.address, ZeroEx.toBaseUnitAmount(new BigNumber(1), 18), { from: coinbase });
|
|
|
|
// Fund the maker on exchange side
|
|
await zrx.setBalance(maker, ZeroEx.toBaseUnitAmount(new BigNumber(1), 18), { from: coinbase });
|
|
// Set the allowance for the maker on Exchange side
|
|
await zrx.approve(tokenTransferProxy.address, INITIAL_ALLOWANCE, { from: maker });
|
|
|
|
const amountGive = ZeroEx.toBaseUnitAmount(new BigNumber(2), 18);
|
|
// Fund the maker on EtherDelta side
|
|
await weth.setBalance(edMaker, ZeroEx.toBaseUnitAmount(new BigNumber(2), 18), { from: coinbase });
|
|
// Set the allowance for the maker on EtherDelta side
|
|
await weth.approve(etherDelta.address, INITIAL_ALLOWANCE, { from: edMaker });
|
|
// Deposit maker funds into EtherDelta
|
|
await etherDelta.depositToken(weth.address, amountGive, { from: edMaker });
|
|
|
|
const amountGet = ZeroEx.toBaseUnitAmount(new BigNumber(1), 18);
|
|
// Fund the front runner on EtherDelta side
|
|
await zrx.setBalance(edFrontRunner, amountGet, { from: coinbase });
|
|
// Set the allowance for the maker on EtherDelta side
|
|
await zrx.approve(etherDelta.address, INITIAL_ALLOWANCE, { from: edFrontRunner });
|
|
// Deposit front runner funds into EtherDelta
|
|
await etherDelta.depositToken(zrx.address, amountGet, { from: edFrontRunner });
|
|
});
|
|
beforeEach(async () => {
|
|
await blockchainLifecycle.startAsync();
|
|
});
|
|
afterEach(async () => {
|
|
await blockchainLifecycle.revertAsync();
|
|
});
|
|
describe('makeAtomicTrade', () => {
|
|
let addresses: string[];
|
|
let values: BigNumber[];
|
|
let v: number[];
|
|
let r: string[];
|
|
let s: string[];
|
|
let tokenGet: string;
|
|
let tokenGive: string;
|
|
let amountGet: BigNumber;
|
|
let amountGive: BigNumber;
|
|
let expires: BigNumber;
|
|
let nonce: BigNumber;
|
|
let edSignature: ECSignature;
|
|
before(async () => {
|
|
order = await orderFactory.newSignedOrderAsync();
|
|
const shouldAddPersonalMessagePrefix = false;
|
|
tokenGet = zrx.address;
|
|
amountGet = ZeroEx.toBaseUnitAmount(new BigNumber(1), 18);
|
|
tokenGive = weth.address;
|
|
amountGive = ZeroEx.toBaseUnitAmount(new BigNumber(2), 18);
|
|
const blockNumber = await web3Wrapper.getBlockNumberAsync();
|
|
expires = new BigNumber(blockNumber + 10);
|
|
nonce = new BigNumber(42);
|
|
const edOrderHash = `0x${crypto
|
|
.solSHA256([etherDelta.address, tokenGet, amountGet, tokenGive, amountGive, expires, nonce])
|
|
.toString('hex')}`;
|
|
edSignature = await zeroEx.signOrderHashAsync(edOrderHash, edMaker, shouldAddPersonalMessagePrefix);
|
|
addresses = [
|
|
order.params.maker,
|
|
order.params.taker,
|
|
order.params.makerToken,
|
|
order.params.takerToken,
|
|
order.params.feeRecipient,
|
|
tokenGet,
|
|
tokenGive,
|
|
edMaker,
|
|
];
|
|
const fillTakerTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(1), 18);
|
|
const edFillAmount = ZeroEx.toBaseUnitAmount(new BigNumber(1), 18);
|
|
values = [
|
|
order.params.makerTokenAmount,
|
|
order.params.takerTokenAmount,
|
|
order.params.makerFee,
|
|
order.params.takerFee,
|
|
order.params.expirationTimestampInSec,
|
|
order.params.salt,
|
|
fillTakerTokenAmount,
|
|
amountGet,
|
|
amountGive,
|
|
expires,
|
|
nonce,
|
|
edFillAmount,
|
|
];
|
|
v = [order.params.v as number, edSignature.v];
|
|
r = [order.params.r as string, edSignature.r];
|
|
s = [order.params.s as string, edSignature.s];
|
|
});
|
|
it('1', async () => {
|
|
const txHash = await arbitrage.makeAtomicTrade(addresses, values, v, r, s, { from: coinbase });
|
|
const res = await zeroEx.awaitTransactionMinedAsync(txHash);
|
|
const postBalance = await weth.balanceOf(arbitrage.address);
|
|
expect(postBalance).to.be.bignumber.equal(ZeroEx.toBaseUnitAmount(new BigNumber(2), 18));
|
|
});
|
|
it('2', async () => {
|
|
// Front-running transaction
|
|
await etherDelta.trade(
|
|
tokenGet,
|
|
amountGet,
|
|
tokenGive,
|
|
amountGive,
|
|
expires,
|
|
nonce,
|
|
edMaker,
|
|
edSignature.v,
|
|
edSignature.r,
|
|
edSignature.s,
|
|
ZeroEx.toBaseUnitAmount(new BigNumber(1), 18),
|
|
{ from: edFrontRunner },
|
|
);
|
|
return expect(arbitrage.makeAtomicTrade(addresses, values, v, r, s, { from: coinbase })).to.be.rejectedWith(
|
|
constants.REVERT,
|
|
);
|
|
});
|
|
});
|
|
});
|