aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLeonid Logvinov <logvinov.leon@gmail.com>2018-02-03 01:30:17 +0800
committerLeonid Logvinov <logvinov.leon@gmail.com>2018-03-02 04:21:08 +0800
commit2d0940589eb9659cea5179d5b1665ce717ecdf2e (patch)
treebad9c7e51020ec1f3a3cef25993bf9163c7e8c2f
parent451a0dacbe85d7a0d16ef007c1eb945a4c1977cb (diff)
downloaddexon-sol-tools-2d0940589eb9659cea5179d5b1665ce717ecdf2e.tar
dexon-sol-tools-2d0940589eb9659cea5179d5b1665ce717ecdf2e.tar.gz
dexon-sol-tools-2d0940589eb9659cea5179d5b1665ce717ecdf2e.tar.bz2
dexon-sol-tools-2d0940589eb9659cea5179d5b1665ce717ecdf2e.tar.lz
dexon-sol-tools-2d0940589eb9659cea5179d5b1665ce717ecdf2e.tar.xz
dexon-sol-tools-2d0940589eb9659cea5179d5b1665ce717ecdf2e.tar.zst
dexon-sol-tools-2d0940589eb9659cea5179d5b1665ce717ecdf2e.zip
Initial implementation of Arbitrage contract with tests
-rw-r--r--lerna.json5
-rw-r--r--packages/contracts/contracts/tutorials/AccountLevels.sol11
-rw-r--r--packages/contracts/contracts/tutorials/Arbitrage.sol103
-rw-r--r--packages/contracts/contracts/tutorials/EtherDelta.sol168
-rw-r--r--packages/contracts/globals.d.ts1
-rw-r--r--packages/contracts/test/tutorials/arbitrage.ts207
-rw-r--r--packages/contracts/util/crypto.ts8
-rw-r--r--packages/contracts/util/types.ts3
-rw-r--r--packages/deployer/src/compiler.ts3
9 files changed, 508 insertions, 1 deletions
diff --git a/lerna.json b/lerna.json
index dbe42bcb1..5f975fff9 100644
--- a/lerna.json
+++ b/lerna.json
@@ -1,6 +1,11 @@
{
"lerna": "2.5.1",
"packages": ["packages/*"],
+ "commands": {
+ "publish": {
+ "allowBranch": "development"
+ }
+ },
"version": "independent",
"commands": {
"publish": {
diff --git a/packages/contracts/contracts/tutorials/AccountLevels.sol b/packages/contracts/contracts/tutorials/AccountLevels.sol
new file mode 100644
index 000000000..2214ed6b3
--- /dev/null
+++ b/packages/contracts/contracts/tutorials/AccountLevels.sol
@@ -0,0 +1,11 @@
+pragma solidity ^0.4.19;
+
+contract AccountLevels {
+ //given a user, returns an account level
+ //0 = regular user (pays take fee and make fee)
+ //1 = market maker silver (pays take fee, no make fee, gets rebate)
+ //2 = market maker gold (pays take fee, no make fee, gets entire counterparty's take fee as rebate)
+ function accountLevel(address user) constant returns(uint) {
+ return 0;
+ }
+}
diff --git a/packages/contracts/contracts/tutorials/Arbitrage.sol b/packages/contracts/contracts/tutorials/Arbitrage.sol
new file mode 100644
index 000000000..726696966
--- /dev/null
+++ b/packages/contracts/contracts/tutorials/Arbitrage.sol
@@ -0,0 +1,103 @@
+pragma solidity ^0.4.19;
+pragma experimental ABIEncoderV2;
+
+import "../Exchange.sol";
+import "../tokens/Token.sol";
+import "./EtherDelta.sol";
+import "../tokens/WETH9.sol";
+
+contract Arbitrage is Ownable {
+
+ Exchange exchange;
+ EtherDelta etherDelta;
+ address proxyAddress;
+
+ uint constant MAX_UINT = 2**256 - 1;
+
+ function Arbitrage(address _exchangeAddress, address _etherDeltaAddress, address _proxyAddress) {
+ exchange = Exchange(_exchangeAddress);
+ etherDelta = EtherDelta(_etherDeltaAddress);
+ proxyAddress = _proxyAddress;
+ }
+
+ function setAllowances(address tokenAddress) public onlyOwner {
+ Token token = Token(tokenAddress);
+ token.approve(address(etherDelta), MAX_UINT);
+ token.approve(proxyAddress, MAX_UINT);
+ token.approve(owner, MAX_UINT);
+ }
+
+ /*
+ * I 愛 the limitations on Solidity stack size!
+ *
+ * addresses
+ * 0..4 orderAddresses
+ * 5 tokenGet
+ * 6 tokenGive
+ * 7 user
+ *
+ * values
+ * 0..5 orderValues
+ * 6 fillTakerTokenAmount
+ * 7 amountGet
+ * 8 amountGive
+ * 9 expires
+ * 10 nonce
+ * 11 amount
+
+ * signature
+ * exchange then etherDelta
+ */
+ function makeAtomicTrade(
+ address[8] addresses, uint[12] values,
+ uint8[2] v, bytes32[2] r, bytes32[2] s
+ ) public onlyOwner {
+ makeExchangeTrade(addresses, values, v, r, s);
+ makeEtherDeltaTrade(addresses, values, v, r, s);
+ }
+
+ function makeEtherDeltaTrade(
+ address[8] addresses, uint[12] values,
+ uint8[2] v, bytes32[2] r, bytes32[2] s
+ ) internal {
+ uint amount = values[11];
+ etherDelta.depositToken(addresses[5], values[7]);
+ etherDelta.trade(
+ addresses[5],
+ values[7],
+ addresses[6],
+ values[8],
+ values[9],
+ values[10],
+ addresses[7],
+ v[1],
+ r[1],
+ s[1],
+ amount
+ );
+ etherDelta.withdrawToken(addresses[6], values[8]);
+ }
+
+ function makeExchangeTrade(
+ address[8] addresses, uint[12] values,
+ uint8[2] v, bytes32[2] r, bytes32[2] s
+ ) internal {
+ address[5] memory orderAddresses = [
+ addresses[0],
+ addresses[1],
+ addresses[2],
+ addresses[3],
+ addresses[4]
+ ];
+ uint[6] memory orderValues = [
+ values[0],
+ values[1],
+ values[2],
+ values[3],
+ values[4],
+ values[5]
+ ];
+ uint fillTakerTokenAmount = values[6];
+ exchange.fillOrKillOrder(orderAddresses, orderValues, fillTakerTokenAmount, v[0], r[0], s[0]);
+ }
+}
diff --git a/packages/contracts/contracts/tutorials/EtherDelta.sol b/packages/contracts/contracts/tutorials/EtherDelta.sol
new file mode 100644
index 000000000..74b4c08e8
--- /dev/null
+++ b/packages/contracts/contracts/tutorials/EtherDelta.sol
@@ -0,0 +1,168 @@
+pragma solidity ^0.4.19;
+
+import "../utils/SafeMath.sol";
+import "./AccountLevels.sol";
+import "../tokens/Token.sol";
+
+contract EtherDelta is SafeMath {
+ address public admin; //the admin address
+ address public feeAccount; //the account that will receive fees
+ address public accountLevelsAddr; //the address of the AccountLevels contract
+ uint public feeMake; //percentage times (1 ether)
+ uint public feeTake; //percentage times (1 ether)
+ uint public feeRebate; //percentage times (1 ether)
+ mapping (address => mapping (address => uint)) public tokens; //mapping of token addresses to mapping of account balances (token=0 means Ether)
+ mapping (address => mapping (bytes32 => bool)) public orders; //mapping of user accounts to mapping of order hashes to booleans (true = submitted by user, equivalent to offchain signature)
+ mapping (address => mapping (bytes32 => uint)) public orderFills; //mapping of user accounts to mapping of order hashes to uints (amount of order that has been filled)
+
+ event Order(address tokenGet, uint amountGet, address tokenGive, uint amountGive, uint expires, uint nonce, address user);
+ event Cancel(address tokenGet, uint amountGet, address tokenGive, uint amountGive, uint expires, uint nonce, address user, uint8 v, bytes32 r, bytes32 s);
+ event Trade(address tokenGet, uint amountGet, address tokenGive, uint amountGive, address get, address give);
+ event Deposit(address token, address user, uint amount, uint balance);
+ event Withdraw(address token, address user, uint amount, uint balance);
+
+ function EtherDelta(address admin_, address feeAccount_, address accountLevelsAddr_, uint feeMake_, uint feeTake_, uint feeRebate_) {
+ admin = admin_;
+ feeAccount = feeAccount_;
+ accountLevelsAddr = accountLevelsAddr_;
+ feeMake = feeMake_;
+ feeTake = feeTake_;
+ feeRebate = feeRebate_;
+ }
+
+ function() {
+ throw;
+ }
+
+ function changeAdmin(address admin_) {
+ if (msg.sender != admin) throw;
+ admin = admin_;
+ }
+
+ function changeAccountLevelsAddr(address accountLevelsAddr_) {
+ if (msg.sender != admin) throw;
+ accountLevelsAddr = accountLevelsAddr_;
+ }
+
+ function changeFeeAccount(address feeAccount_) {
+ if (msg.sender != admin) throw;
+ feeAccount = feeAccount_;
+ }
+
+ function changeFeeMake(uint feeMake_) {
+ if (msg.sender != admin) throw;
+ if (feeMake_ > feeMake) throw;
+ feeMake = feeMake_;
+ }
+
+ function changeFeeTake(uint feeTake_) {
+ if (msg.sender != admin) throw;
+ if (feeTake_ > feeTake || feeTake_ < feeRebate) throw;
+ feeTake = feeTake_;
+ }
+
+ function changeFeeRebate(uint feeRebate_) {
+ if (msg.sender != admin) throw;
+ if (feeRebate_ < feeRebate || feeRebate_ > feeTake) throw;
+ feeRebate = feeRebate_;
+ }
+
+ function deposit() payable {
+ tokens[0][msg.sender] = safeAdd(tokens[0][msg.sender], msg.value);
+ Deposit(0, msg.sender, msg.value, tokens[0][msg.sender]);
+ }
+
+ function withdraw(uint amount) {
+ if (tokens[0][msg.sender] < amount) throw;
+ tokens[0][msg.sender] = safeSub(tokens[0][msg.sender], amount);
+ if (!msg.sender.call.value(amount)()) throw;
+ Withdraw(0, msg.sender, amount, tokens[0][msg.sender]);
+ }
+
+ function depositToken(address token, uint amount) {
+ //remember to call Token(address).approve(this, amount) or this contract will not be able to do the transfer on your behalf.
+ if (token==0) throw;
+ if (!Token(token).transferFrom(msg.sender, this, amount)) throw;
+ tokens[token][msg.sender] = safeAdd(tokens[token][msg.sender], amount);
+ Deposit(token, msg.sender, amount, tokens[token][msg.sender]);
+ }
+
+ function withdrawToken(address token, uint amount) {
+ if (token==0) throw;
+ if (tokens[token][msg.sender] < amount) throw;
+ tokens[token][msg.sender] = safeSub(tokens[token][msg.sender], amount);
+ if (!Token(token).transfer(msg.sender, amount)) throw;
+ Withdraw(token, msg.sender, amount, tokens[token][msg.sender]);
+ }
+
+ function balanceOf(address token, address user) constant returns (uint) {
+ return tokens[token][user];
+ }
+
+ function order(address tokenGet, uint amountGet, address tokenGive, uint amountGive, uint expires, uint nonce) {
+ bytes32 hash = sha256(this, tokenGet, amountGet, tokenGive, amountGive, expires, nonce);
+ orders[msg.sender][hash] = true;
+ Order(tokenGet, amountGet, tokenGive, amountGive, expires, nonce, msg.sender);
+ }
+
+ function trade(address tokenGet, uint amountGet, address tokenGive, uint amountGive, uint expires, uint nonce, address user, uint8 v, bytes32 r, bytes32 s, uint amount) {
+ //amount is in amountGet terms
+ bytes32 hash = sha256(this, tokenGet, amountGet, tokenGive, amountGive, expires, nonce);
+ if (!(
+ (orders[user][hash] || ecrecover(sha3("\x19Ethereum Signed Message:\n32", hash),v,r,s) == user) &&
+ block.number <= expires &&
+ safeAdd(orderFills[user][hash], amount) <= amountGet
+ )) throw;
+ tradeBalances(tokenGet, amountGet, tokenGive, amountGive, user, amount);
+ orderFills[user][hash] = safeAdd(orderFills[user][hash], amount);
+ Trade(tokenGet, amount, tokenGive, amountGive * amount / amountGet, user, msg.sender);
+ }
+
+ function tradeBalances(address tokenGet, uint amountGet, address tokenGive, uint amountGive, address user, uint amount) private {
+ uint feeMakeXfer = safeMul(amount, feeMake) / (1 ether);
+ uint feeTakeXfer = safeMul(amount, feeTake) / (1 ether);
+ uint feeRebateXfer = 0;
+ if (accountLevelsAddr != 0x0) {
+ uint accountLevel = AccountLevels(accountLevelsAddr).accountLevel(user);
+ if (accountLevel==1) feeRebateXfer = safeMul(amount, feeRebate) / (1 ether);
+ if (accountLevel==2) feeRebateXfer = feeTakeXfer;
+ }
+ tokens[tokenGet][msg.sender] = safeSub(tokens[tokenGet][msg.sender], safeAdd(amount, feeTakeXfer));
+ tokens[tokenGet][user] = safeAdd(tokens[tokenGet][user], safeSub(safeAdd(amount, feeRebateXfer), feeMakeXfer));
+ tokens[tokenGet][feeAccount] = safeAdd(tokens[tokenGet][feeAccount], safeSub(safeAdd(feeMakeXfer, feeTakeXfer), feeRebateXfer));
+ tokens[tokenGive][user] = safeSub(tokens[tokenGive][user], safeMul(amountGive, amount) / amountGet);
+ tokens[tokenGive][msg.sender] = safeAdd(tokens[tokenGive][msg.sender], safeMul(amountGive, amount) / amountGet);
+ }
+
+ function testTrade(address tokenGet, uint amountGet, address tokenGive, uint amountGive, uint expires, uint nonce, address user, uint8 v, bytes32 r, bytes32 s, uint amount, address sender) constant returns(bool) {
+ if (!(
+ tokens[tokenGet][sender] >= amount &&
+ availableVolume(tokenGet, amountGet, tokenGive, amountGive, expires, nonce, user, v, r, s) >= amount
+ )) return false;
+ return true;
+ }
+
+ function availableVolume(address tokenGet, uint amountGet, address tokenGive, uint amountGive, uint expires, uint nonce, address user, uint8 v, bytes32 r, bytes32 s) constant returns(uint) {
+ bytes32 hash = sha256(this, tokenGet, amountGet, tokenGive, amountGive, expires, nonce);
+ if (!(
+ (orders[user][hash] || ecrecover(sha3("\x19Ethereum Signed Message:\n32", hash),v,r,s) == user) &&
+ block.number <= expires
+ )) return 0;
+ uint available1 = safeSub(amountGet, orderFills[user][hash]);
+ uint available2 = safeMul(tokens[tokenGive][user], amountGet) / amountGive;
+ if (available1<available2) return available1;
+ return available2;
+ }
+
+ function amountFilled(address tokenGet, uint amountGet, address tokenGive, uint amountGive, uint expires, uint nonce, address user, uint8 v, bytes32 r, bytes32 s) constant returns(uint) {
+ bytes32 hash = sha256(this, tokenGet, amountGet, tokenGive, amountGive, expires, nonce);
+ return orderFills[user][hash];
+ }
+
+ function cancelOrder(address tokenGet, uint amountGet, address tokenGive, uint amountGive, uint expires, uint nonce, uint8 v, bytes32 r, bytes32 s) {
+ bytes32 hash = sha256(this, tokenGet, amountGet, tokenGive, amountGive, expires, nonce);
+ if (!(orders[msg.sender][hash] || ecrecover(sha3("\x19Ethereum Signed Message:\n32", hash),v,r,s) == msg.sender)) throw;
+ orderFills[msg.sender][hash] = amountGet;
+ Cancel(tokenGet, amountGet, tokenGive, amountGive, expires, nonce, msg.sender, v, r, s);
+ }
+}
diff --git a/packages/contracts/globals.d.ts b/packages/contracts/globals.d.ts
index 0e6586a4b..c6597054a 100644
--- a/packages/contracts/globals.d.ts
+++ b/packages/contracts/globals.d.ts
@@ -30,5 +30,6 @@ declare module 'web3-eth-abi' {
declare module 'ethereumjs-abi' {
const soliditySHA3: (argTypes: string[], args: any[]) => Buffer;
+ const soliditySHA256: (argTypes: string[], args: any[]) => Buffer;
const methodID: (name: string, types: string[]) => Buffer;
}
diff --git a/packages/contracts/test/tutorials/arbitrage.ts b/packages/contracts/test/tutorials/arbitrage.ts
new file mode 100644
index 000000000..b2f1c338a
--- /dev/null
+++ b/packages/contracts/test/tutorials/arbitrage.ts
@@ -0,0 +1,207 @@
+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,
+ );
+ });
+ });
+});
diff --git a/packages/contracts/util/crypto.ts b/packages/contracts/util/crypto.ts
index 9173df643..97b8f5643 100644
--- a/packages/contracts/util/crypto.ts
+++ b/packages/contracts/util/crypto.ts
@@ -13,6 +13,12 @@ export const crypto = {
* valid Ethereum address -> address
*/
solSHA3(args: any[]): Buffer {
+ return crypto._solHash(args, ABI.soliditySHA3);
+ },
+ solSHA256(args: any[]): Buffer {
+ return crypto._solHash(args, ABI.soliditySHA256);
+ },
+ _solHash(args: any[], hashFunction: (types: string[], values: any[]) => Buffer) {
const argTypes: string[] = [];
_.each(args, (arg, i) => {
const isNumber = _.isFinite(arg);
@@ -31,7 +37,7 @@ export const crypto = {
throw new Error(`Unable to guess arg type: ${arg}`);
}
});
- const hash = ABI.soliditySHA3(argTypes, args);
+ const hash = hashFunction(argTypes, args);
return hash;
},
};
diff --git a/packages/contracts/util/types.ts b/packages/contracts/util/types.ts
index d6e4c587d..61a19acb4 100644
--- a/packages/contracts/util/types.ts
+++ b/packages/contracts/util/types.ts
@@ -96,6 +96,9 @@ export enum ContractName {
EtherToken = 'WETH9',
MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress = 'MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress',
MaliciousToken = 'MaliciousToken',
+ AccountLevels = 'AccountLevels',
+ EtherDelta = 'EtherDelta',
+ Arbitrage = 'Arbitrage',
}
export interface Artifact {
diff --git a/packages/deployer/src/compiler.ts b/packages/deployer/src/compiler.ts
index 149ca5d6d..ea66444b2 100644
--- a/packages/deployer/src/compiler.ts
+++ b/packages/deployer/src/compiler.ts
@@ -209,6 +209,9 @@ export class Compiler {
const normalizedErrMsg = Compiler._getNormalizedErrMsg(errMsg);
this._solcErrors.add(normalizedErrMsg);
});
+ if (!_.isEmpty(compiled.errors)) {
+ return;
+ }
}
const contractName = path.basename(fileName, constants.SOLIDITY_FILE_EXTENSION);