aboutsummaryrefslogblamecommitdiffstats
path: root/packages/contracts/test/tutorials/arbitrage.ts
blob: b2f1c338a852ca08e2201ba19ad0e2bc9458e143 (plain) (tree)














































































































































































































                                                                                                                        
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,
            );
        });
    });
});