From 2d0940589eb9659cea5179d5b1665ce717ecdf2e Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Fri, 2 Feb 2018 18:30:17 +0100 Subject: Initial implementation of Arbitrage contract with tests --- lerna.json | 5 + .../contracts/tutorials/AccountLevels.sol | 11 ++ .../contracts/contracts/tutorials/Arbitrage.sol | 103 ++++++++++ .../contracts/contracts/tutorials/EtherDelta.sol | 168 +++++++++++++++++ packages/contracts/globals.d.ts | 1 + packages/contracts/test/tutorials/arbitrage.ts | 207 +++++++++++++++++++++ packages/contracts/util/crypto.ts | 8 +- packages/contracts/util/types.ts | 3 + packages/deployer/src/compiler.ts | 3 + 9 files changed, 508 insertions(+), 1 deletion(-) create mode 100644 packages/contracts/contracts/tutorials/AccountLevels.sol create mode 100644 packages/contracts/contracts/tutorials/Arbitrage.sol create mode 100644 packages/contracts/contracts/tutorials/EtherDelta.sol create mode 100644 packages/contracts/test/tutorials/arbitrage.ts 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 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); -- cgit v1.2.3 From 45df9de5b5b4ab6cfd4fda1b568f8109ec2c0371 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Fri, 2 Feb 2018 18:38:53 +0100 Subject: Assert that the balances don't change if arbitrage fails --- packages/contracts/test/tutorials/arbitrage.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/contracts/test/tutorials/arbitrage.ts b/packages/contracts/test/tutorials/arbitrage.ts index b2f1c338a..68b8f555e 100644 --- a/packages/contracts/test/tutorials/arbitrage.ts +++ b/packages/contracts/test/tutorials/arbitrage.ts @@ -22,7 +22,7 @@ const web3 = web3Factory.create(); const web3Wrapper = new Web3Wrapper(web3.currentProvider); const blockchainLifecycle = new BlockchainLifecycle(); -describe.only('Arbitrage', () => { +describe('Arbitrage', () => { let coinbase: string; let maker: string; let edMaker: string; @@ -177,13 +177,14 @@ describe.only('Arbitrage', () => { r = [order.params.r as string, edSignature.r]; s = [order.params.s as string, edSignature.s]; }); - it('1', async () => { + it('should successfully execute the arbitrage if not front-runner', 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 () => { + it('should fail and revert if front-runned', async () => { + const preBalance = await weth.balanceOf(arbitrage.address); // Front-running transaction await etherDelta.trade( tokenGet, @@ -199,9 +200,11 @@ describe.only('Arbitrage', () => { ZeroEx.toBaseUnitAmount(new BigNumber(1), 18), { from: edFrontRunner }, ); - return expect(arbitrage.makeAtomicTrade(addresses, values, v, r, s, { from: coinbase })).to.be.rejectedWith( + await expect(arbitrage.makeAtomicTrade(addresses, values, v, r, s, { from: coinbase })).to.be.rejectedWith( constants.REVERT, ); + const postBalance = await weth.balanceOf(arbitrage.address); + expect(preBalance).to.be.bignumber.equal(postBalance); }); }); }); -- cgit v1.2.3 From c90770472e19759d0bb2db49be3d8d9340f76bac Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Fri, 2 Feb 2018 18:46:58 +0100 Subject: Remove false-positive linter failure because of chai-as-pronmised incorrect types --- packages/contracts/test/tutorials/arbitrage.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/contracts/test/tutorials/arbitrage.ts b/packages/contracts/test/tutorials/arbitrage.ts index 68b8f555e..d2ab076c9 100644 --- a/packages/contracts/test/tutorials/arbitrage.ts +++ b/packages/contracts/test/tutorials/arbitrage.ts @@ -200,6 +200,7 @@ describe('Arbitrage', () => { ZeroEx.toBaseUnitAmount(new BigNumber(1), 18), { from: edFrontRunner }, ); + // tslint:disable-next-line:await-promise await expect(arbitrage.makeAtomicTrade(addresses, values, v, r, s, { from: coinbase })).to.be.rejectedWith( constants.REVERT, ); -- cgit v1.2.3 From e42891ae5b1b535d4376fc31078f15d6c3c9cb77 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Mon, 5 Feb 2018 15:12:01 +0100 Subject: Move tutorials contracts to src folder --- .../contracts/tutorials/AccountLevels.sol | 11 -- .../contracts/contracts/tutorials/Arbitrage.sol | 103 ------------- .../contracts/contracts/tutorials/EtherDelta.sol | 168 --------------------- .../tutorials/AcccountLevels/AccountLevels.sol | 11 ++ .../src/current/tutorials/Arbitrage/Arbitrage.sol | 102 +++++++++++++ .../current/tutorials/EtherDelta/EtherDelta.sol | 168 +++++++++++++++++++++ 6 files changed, 281 insertions(+), 282 deletions(-) delete mode 100644 packages/contracts/contracts/tutorials/AccountLevels.sol delete mode 100644 packages/contracts/contracts/tutorials/Arbitrage.sol delete mode 100644 packages/contracts/contracts/tutorials/EtherDelta.sol create mode 100644 packages/contracts/src/current/tutorials/AcccountLevels/AccountLevels.sol create mode 100644 packages/contracts/src/current/tutorials/Arbitrage/Arbitrage.sol create mode 100644 packages/contracts/src/current/tutorials/EtherDelta/EtherDelta.sol diff --git a/packages/contracts/contracts/tutorials/AccountLevels.sol b/packages/contracts/contracts/tutorials/AccountLevels.sol deleted file mode 100644 index 2214ed6b3..000000000 --- a/packages/contracts/contracts/tutorials/AccountLevels.sol +++ /dev/null @@ -1,11 +0,0 @@ -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 deleted file mode 100644 index 726696966..000000000 --- a/packages/contracts/contracts/tutorials/Arbitrage.sol +++ /dev/null @@ -1,103 +0,0 @@ -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 deleted file mode 100644 index 74b4c08e8..000000000 --- a/packages/contracts/contracts/tutorials/EtherDelta.sol +++ /dev/null @@ -1,168 +0,0 @@ -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 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 Date: Mon, 5 Feb 2018 15:16:29 +0100 Subject: Put all ED contracts in one folder --- .../src/current/tutorials/AcccountLevels/AccountLevels.sol | 11 ----------- .../src/current/tutorials/EtherDelta/AccountLevels.sol | 11 +++++++++++ .../contracts/src/current/tutorials/EtherDelta/EtherDelta.sol | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) delete mode 100644 packages/contracts/src/current/tutorials/AcccountLevels/AccountLevels.sol create mode 100644 packages/contracts/src/current/tutorials/EtherDelta/AccountLevels.sol diff --git a/packages/contracts/src/current/tutorials/AcccountLevels/AccountLevels.sol b/packages/contracts/src/current/tutorials/AcccountLevels/AccountLevels.sol deleted file mode 100644 index 2214ed6b3..000000000 --- a/packages/contracts/src/current/tutorials/AcccountLevels/AccountLevels.sol +++ /dev/null @@ -1,11 +0,0 @@ -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/src/current/tutorials/EtherDelta/AccountLevels.sol b/packages/contracts/src/current/tutorials/EtherDelta/AccountLevels.sol new file mode 100644 index 000000000..2214ed6b3 --- /dev/null +++ b/packages/contracts/src/current/tutorials/EtherDelta/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/src/current/tutorials/EtherDelta/EtherDelta.sol b/packages/contracts/src/current/tutorials/EtherDelta/EtherDelta.sol index 2b58a4a68..515e3342c 100644 --- a/packages/contracts/src/current/tutorials/EtherDelta/EtherDelta.sol +++ b/packages/contracts/src/current/tutorials/EtherDelta/EtherDelta.sol @@ -1,7 +1,7 @@ pragma solidity ^0.4.19; import { SafeMath } from "../../utils/SafeMath/SafeMath.sol"; -import { AccountLevels } from "../AccountLevels/AccountLevels.sol"; +import { AccountLevels } from "./AccountLevels.sol"; import { Token } from "../../tokens/Token/Token.sol"; contract EtherDelta is SafeMath { -- cgit v1.2.3 From 413e8b606203ece71b732732b160541f78ae0008 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Mon, 5 Feb 2018 15:17:10 +0100 Subject: Fix the comment --- packages/contracts/test/tutorials/arbitrage.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/contracts/test/tutorials/arbitrage.ts b/packages/contracts/test/tutorials/arbitrage.ts index d2ab076c9..ee8bb0e10 100644 --- a/packages/contracts/test/tutorials/arbitrage.ts +++ b/packages/contracts/test/tutorials/arbitrage.ts @@ -109,7 +109,7 @@ describe('Arbitrage', () => { 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 + // Set the allowance for the front-runner 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 }); -- cgit v1.2.3 From c6358d5f224cb2d381aa0c8af9bcedffa1cf5fe5 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Mon, 5 Feb 2018 15:19:23 +0100 Subject: Make setAllowances external --- packages/contracts/src/current/tutorials/Arbitrage/Arbitrage.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/contracts/src/current/tutorials/Arbitrage/Arbitrage.sol b/packages/contracts/src/current/tutorials/Arbitrage/Arbitrage.sol index 3ee8b5fba..3ae2e92b8 100644 --- a/packages/contracts/src/current/tutorials/Arbitrage/Arbitrage.sol +++ b/packages/contracts/src/current/tutorials/Arbitrage/Arbitrage.sol @@ -19,7 +19,7 @@ contract Arbitrage is Ownable { proxyAddress = _proxyAddress; } - function setAllowances(address tokenAddress) public onlyOwner { + function setAllowances(address tokenAddress) external onlyOwner { Token token = Token(tokenAddress); token.approve(address(etherDelta), MAX_UINT); token.approve(proxyAddress, MAX_UINT); -- cgit v1.2.3 From 6325fac020d53ed13ab0b09a7a6cbdb12476c03a Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Mon, 5 Feb 2018 15:19:43 +0100 Subject: Change type to uint256 --- packages/contracts/src/current/tutorials/Arbitrage/Arbitrage.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/contracts/src/current/tutorials/Arbitrage/Arbitrage.sol b/packages/contracts/src/current/tutorials/Arbitrage/Arbitrage.sol index 3ae2e92b8..63ea47078 100644 --- a/packages/contracts/src/current/tutorials/Arbitrage/Arbitrage.sol +++ b/packages/contracts/src/current/tutorials/Arbitrage/Arbitrage.sol @@ -11,7 +11,7 @@ contract Arbitrage is Ownable { EtherDelta etherDelta; address proxyAddress; - uint constant MAX_UINT = 2**256 - 1; + uint256 constant MAX_UINT = 2**256 - 1; function Arbitrage(address _exchangeAddress, address _etherDeltaAddress, address _proxyAddress) { exchange = Exchange(_exchangeAddress); -- cgit v1.2.3 From e36fb654b02fc34e1699ea8d0b98ab89726b37d2 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Mon, 5 Feb 2018 15:19:56 +0100 Subject: Fix a typo --- packages/contracts/test/tutorials/arbitrage.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/contracts/test/tutorials/arbitrage.ts b/packages/contracts/test/tutorials/arbitrage.ts index ee8bb0e10..d32b4b344 100644 --- a/packages/contracts/test/tutorials/arbitrage.ts +++ b/packages/contracts/test/tutorials/arbitrage.ts @@ -135,7 +135,6 @@ describe('Arbitrage', () => { 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; @@ -146,6 +145,7 @@ describe('Arbitrage', () => { const edOrderHash = `0x${crypto .solSHA256([etherDelta.address, tokenGet, amountGet, tokenGive, amountGive, expires, nonce]) .toString('hex')}`; + const shouldAddPersonalMessagePrefix = false; edSignature = await zeroEx.signOrderHashAsync(edOrderHash, edMaker, shouldAddPersonalMessagePrefix); addresses = [ order.params.maker, @@ -177,7 +177,7 @@ describe('Arbitrage', () => { r = [order.params.r as string, edSignature.r]; s = [order.params.s as string, edSignature.s]; }); - it('should successfully execute the arbitrage if not front-runner', async () => { + it('should successfully execute the arbitrage if not front-runned', 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); -- cgit v1.2.3 From cb47a51b7053d23d56e5867f224019aed4a3cf1e Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Mon, 5 Feb 2018 15:23:32 +0100 Subject: Make external --- packages/contracts/src/current/tutorials/Arbitrage/Arbitrage.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/contracts/src/current/tutorials/Arbitrage/Arbitrage.sol b/packages/contracts/src/current/tutorials/Arbitrage/Arbitrage.sol index 63ea47078..f355cfefc 100644 --- a/packages/contracts/src/current/tutorials/Arbitrage/Arbitrage.sol +++ b/packages/contracts/src/current/tutorials/Arbitrage/Arbitrage.sol @@ -50,7 +50,7 @@ contract Arbitrage is Ownable { function makeAtomicTrade( address[8] addresses, uint[12] values, uint8[2] v, bytes32[2] r, bytes32[2] s - ) public onlyOwner { + ) external onlyOwner { makeExchangeTrade(addresses, values, v, r, s); makeEtherDeltaTrade(addresses, values, v, r, s); } -- cgit v1.2.3 From 6c5333180d6207f273219de3fb0c269a81e2ae48 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Mon, 5 Feb 2018 15:25:45 +0100 Subject: Pretty-print ED contracts --- .../current/tutorials/EtherDelta/AccountLevels.sol | 14 +- .../current/tutorials/EtherDelta/EtherDelta.sol | 320 ++++++++++----------- 2 files changed, 167 insertions(+), 167 deletions(-) diff --git a/packages/contracts/src/current/tutorials/EtherDelta/AccountLevels.sol b/packages/contracts/src/current/tutorials/EtherDelta/AccountLevels.sol index 2214ed6b3..8d7a930d3 100644 --- a/packages/contracts/src/current/tutorials/EtherDelta/AccountLevels.sol +++ b/packages/contracts/src/current/tutorials/EtherDelta/AccountLevels.sol @@ -1,11 +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; - } + //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/src/current/tutorials/EtherDelta/EtherDelta.sol b/packages/contracts/src/current/tutorials/EtherDelta/EtherDelta.sol index 515e3342c..49847ab48 100644 --- a/packages/contracts/src/current/tutorials/EtherDelta/EtherDelta.sol +++ b/packages/contracts/src/current/tutorials/EtherDelta/EtherDelta.sol @@ -5,164 +5,164 @@ import { AccountLevels } from "./AccountLevels.sol"; import { Token } from "../../tokens/Token/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 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 Date: Mon, 5 Feb 2018 15:33:09 +0100 Subject: Don't pass tokenGet and tokenGive because we can get them from 0x order --- .../src/current/tutorials/Arbitrage/Arbitrage.sol | 20 +++++++++----------- packages/contracts/test/tutorials/arbitrage.ts | 4 +--- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/packages/contracts/src/current/tutorials/Arbitrage/Arbitrage.sol b/packages/contracts/src/current/tutorials/Arbitrage/Arbitrage.sol index f355cfefc..8a744fd8e 100644 --- a/packages/contracts/src/current/tutorials/Arbitrage/Arbitrage.sol +++ b/packages/contracts/src/current/tutorials/Arbitrage/Arbitrage.sol @@ -31,9 +31,7 @@ contract Arbitrage is Ownable { * * addresses * 0..4 orderAddresses - * 5 tokenGet - * 6 tokenGive - * 7 user + * 5 user * * values * 0..5 orderValues @@ -48,7 +46,7 @@ contract Arbitrage is Ownable { * exchange then etherDelta */ function makeAtomicTrade( - address[8] addresses, uint[12] values, + address[6] addresses, uint[12] values, uint8[2] v, bytes32[2] r, bytes32[2] s ) external onlyOwner { makeExchangeTrade(addresses, values, v, r, s); @@ -56,29 +54,29 @@ contract Arbitrage is Ownable { } function makeEtherDeltaTrade( - address[8] addresses, uint[12] values, + address[6] 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.depositToken(addresses[2], values[7]); etherDelta.trade( - addresses[5], + addresses[2], values[7], - addresses[6], + addresses[3], values[8], values[9], values[10], - addresses[7], + addresses[5], v[1], r[1], s[1], amount ); - etherDelta.withdrawToken(addresses[6], values[8]); + etherDelta.withdrawToken(addresses[3], values[8]); } function makeExchangeTrade( - address[8] addresses, uint[12] values, + address[6] addresses, uint[12] values, uint8[2] v, bytes32[2] r, bytes32[2] s ) internal { address[5] memory orderAddresses = [ diff --git a/packages/contracts/test/tutorials/arbitrage.ts b/packages/contracts/test/tutorials/arbitrage.ts index d32b4b344..d438c5f5f 100644 --- a/packages/contracts/test/tutorials/arbitrage.ts +++ b/packages/contracts/test/tutorials/arbitrage.ts @@ -22,7 +22,7 @@ const web3 = web3Factory.create(); const web3Wrapper = new Web3Wrapper(web3.currentProvider); const blockchainLifecycle = new BlockchainLifecycle(); -describe('Arbitrage', () => { +describe.only('Arbitrage', () => { let coinbase: string; let maker: string; let edMaker: string; @@ -153,8 +153,6 @@ describe('Arbitrage', () => { order.params.makerToken, order.params.takerToken, order.params.feeRecipient, - tokenGet, - tokenGive, edMaker, ]; const fillTakerTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(1), 18); -- cgit v1.2.3 From 7be84c6ad324627374adf447e6287deed29a42ed Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Mon, 5 Feb 2018 15:40:51 +0100 Subject: Add comments to Arbitrage contract --- .../src/current/tutorials/Arbitrage/Arbitrage.sol | 49 ++++++++++++---------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/packages/contracts/src/current/tutorials/Arbitrage/Arbitrage.sol b/packages/contracts/src/current/tutorials/Arbitrage/Arbitrage.sol index 8a744fd8e..4c55ddc8e 100644 --- a/packages/contracts/src/current/tutorials/Arbitrage/Arbitrage.sol +++ b/packages/contracts/src/current/tutorials/Arbitrage/Arbitrage.sol @@ -58,21 +58,27 @@ contract Arbitrage is Ownable { uint8[2] v, bytes32[2] r, bytes32[2] s ) internal { uint amount = values[11]; - etherDelta.depositToken(addresses[2], values[7]); + etherDelta.depositToken( + addresses[2], // tokenGet === makerToken + values[7] // amountGet + ); etherDelta.trade( - addresses[2], - values[7], - addresses[3], - values[8], - values[9], - values[10], - addresses[5], + addresses[2], // tokenGet === makerToken + values[7], // amountGet + addresses[3], // tokenGive === takerToken + values[8], // amountGive + values[9], // expires + values[10], // nonce + addresses[5], // user v[1], r[1], s[1], amount ); - etherDelta.withdrawToken(addresses[3], values[8]); + etherDelta.withdrawToken( + addresses[3], // tokenGive === tokenToken + values[8] // amountGive + ); } function makeExchangeTrade( @@ -80,21 +86,22 @@ contract Arbitrage is Ownable { uint8[2] v, bytes32[2] r, bytes32[2] s ) internal { address[5] memory orderAddresses = [ - addresses[0], - addresses[1], - addresses[2], - addresses[3], - addresses[4] + addresses[0], // maker + addresses[1], // taker + addresses[2], // makerToken + addresses[3], // takerToken + addresses[4] // feeRecepient ]; uint[6] memory orderValues = [ - values[0], - values[1], - values[2], - values[3], - values[4], - values[5] + values[0], // makerTokenAmount + values[1], // takerTokenAmount + values[2], // makerFee + values[3], // takerFee + values[4], // expirationTimestampInSec + values[5] // salt ]; - uint fillTakerTokenAmount = values[6]; + uint fillTakerTokenAmount = values[6]; // fillTakerTokenAmount + // Execute Exchange trade. It either succeeds in full or fails and reverts all the changes. exchange.fillOrKillOrder(orderAddresses, orderValues, fillTakerTokenAmount, v[0], r[0], s[0]); } } -- cgit v1.2.3 From d5ca00de9588ee81a1d464a0a8aa5561333c7633 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Mon, 5 Feb 2018 15:45:10 +0100 Subject: Improve the comments --- packages/contracts/src/current/tutorials/Arbitrage/Arbitrage.sol | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/contracts/src/current/tutorials/Arbitrage/Arbitrage.sol b/packages/contracts/src/current/tutorials/Arbitrage/Arbitrage.sol index 4c55ddc8e..c2dea4e3e 100644 --- a/packages/contracts/src/current/tutorials/Arbitrage/Arbitrage.sol +++ b/packages/contracts/src/current/tutorials/Arbitrage/Arbitrage.sol @@ -5,6 +5,8 @@ import { EtherDelta } from "../EtherDelta/EtherDelta.sol"; import { Ownable } from "../../utils/Ownable/Ownable.sol"; import { Token } from "../../tokens/Token/Token.sol"; +/// @title Arbitrage - Facilitates atomic arbitrage of ERC20 tokens between EtherDelta and 0x Exchange contract. +/// @author Leonid Logvinov - contract Arbitrage is Ownable { Exchange exchange; @@ -27,7 +29,8 @@ contract Arbitrage is Ownable { } /* - * I 愛 the limitations on Solidity stack size! + * Because of the limits on the number of local variables in Solidity we need to compress parameters while loosing + * readability. Scheme of the parameter layout: * * addresses * 0..4 orderAddresses -- cgit v1.2.3 From ae209677ded47baf9893e714629a145db7273521 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Mon, 5 Feb 2018 15:47:24 +0100 Subject: Improve the comments --- packages/contracts/src/current/tutorials/Arbitrage/Arbitrage.sol | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/contracts/src/current/tutorials/Arbitrage/Arbitrage.sol b/packages/contracts/src/current/tutorials/Arbitrage/Arbitrage.sol index c2dea4e3e..a9f3c22e6 100644 --- a/packages/contracts/src/current/tutorials/Arbitrage/Arbitrage.sol +++ b/packages/contracts/src/current/tutorials/Arbitrage/Arbitrage.sol @@ -21,6 +21,10 @@ contract Arbitrage is Ownable { proxyAddress = _proxyAddress; } + /* + * Makes token tradeable by setting an allowance for etherDelta and 0x proxy contract. + * Also sets an allowance for the owner of the contracts therefore allowing to withdraw tokens. + */ function setAllowances(address tokenAddress) external onlyOwner { Token token = Token(tokenAddress); token.approve(address(etherDelta), MAX_UINT); -- cgit v1.2.3 From fd5e231a93fa6181c954b54c2c3442c1318132b4 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Mon, 5 Feb 2018 16:11:09 +0100 Subject: Remove only --- packages/contracts/test/tutorials/arbitrage.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/contracts/test/tutorials/arbitrage.ts b/packages/contracts/test/tutorials/arbitrage.ts index d438c5f5f..7da84ddb9 100644 --- a/packages/contracts/test/tutorials/arbitrage.ts +++ b/packages/contracts/test/tutorials/arbitrage.ts @@ -22,7 +22,7 @@ const web3 = web3Factory.create(); const web3Wrapper = new Web3Wrapper(web3.currentProvider); const blockchainLifecycle = new BlockchainLifecycle(); -describe.only('Arbitrage', () => { +describe('Arbitrage', () => { let coinbase: string; let maker: string; let edMaker: string; -- cgit v1.2.3 From 6323496945f28333d333b454f9dd481e70177974 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Mon, 5 Feb 2018 16:21:54 +0100 Subject: Make tests slightly nicer --- packages/contracts/test/tutorials/arbitrage.ts | 39 +++++++++++++++----------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/packages/contracts/test/tutorials/arbitrage.ts b/packages/contracts/test/tutorials/arbitrage.ts index 7da84ddb9..94589c43a 100644 --- a/packages/contracts/test/tutorials/arbitrage.ts +++ b/packages/contracts/test/tutorials/arbitrage.ts @@ -27,6 +27,10 @@ describe('Arbitrage', () => { let maker: string; let edMaker: string; let edFrontRunner: string; + let amountGet: BigNumber; + let amountGive: BigNumber; + let makerTokenAmount: BigNumber; + let takerTokenAmount: BigNumber; const feeRecipient = ZeroEx.NULL_ADDRESS; const INITIAL_BALANCE = ZeroEx.toBaseUnitAmount(new BigNumber(10000), 18); const INITIAL_ALLOWANCE = ZeroEx.toBaseUnitAmount(new BigNumber(10000), 18); @@ -42,6 +46,10 @@ describe('Arbitrage', () => { let zeroEx: ZeroEx; + // From a bird's eye view - we create two orders. + // 0x order of 1 ZRX (maker) for 1 WETH (taker) + // ED order of 2 WETH (tokenGive) for 1 ZRX (tokenGet) + // And then we do an atomic arbitrage between them which gives us 1 WETH. before(async () => { const accounts = await web3Wrapper.getAvailableAddressesAsync(); [coinbase, maker, edMaker, edFrontRunner] = accounts; @@ -69,14 +77,16 @@ describe('Arbitrage', () => { }); exWrapper = new ExchangeWrapper(exchange, zeroEx); + makerTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(1), 18); + takerTokenAmount = makerTokenAmount; 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), + makerTokenAmount, + takerTokenAmount, makerFee: new BigNumber(0), takerFee: new BigNumber(0), }; @@ -91,22 +101,22 @@ describe('Arbitrage', () => { 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 }); + await weth.setBalance(arbitrage.address, takerTokenAmount, { from: coinbase }); // Fund the maker on exchange side - await zrx.setBalance(maker, ZeroEx.toBaseUnitAmount(new BigNumber(1), 18), { from: coinbase }); + await zrx.setBalance(maker, makerTokenAmount, { 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); + 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 }); + await weth.setBalance(edMaker, amountGive, { 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); + amountGet = makerTokenAmount; // Fund the front runner on EtherDelta side await zrx.setBalance(edFrontRunner, amountGet, { from: coinbase }); // Set the allowance for the front-runner on EtherDelta side @@ -128,19 +138,16 @@ describe('Arbitrage', () => { 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(); 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); + const ED_ORDER_EXPIRATION_IN_BLOCKS = 10; + expires = new BigNumber(blockNumber + ED_ORDER_EXPIRATION_IN_BLOCKS); nonce = new BigNumber(42); const edOrderHash = `0x${crypto .solSHA256([etherDelta.address, tokenGet, amountGet, tokenGive, amountGive, expires, nonce]) @@ -155,8 +162,8 @@ describe('Arbitrage', () => { order.params.feeRecipient, edMaker, ]; - const fillTakerTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(1), 18); - const edFillAmount = ZeroEx.toBaseUnitAmount(new BigNumber(1), 18); + const fillTakerTokenAmount = takerTokenAmount; + const edFillAmount = makerTokenAmount; values = [ order.params.makerTokenAmount, order.params.takerTokenAmount, @@ -179,7 +186,7 @@ describe('Arbitrage', () => { 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)); + expect(postBalance).to.be.bignumber.equal(amountGive); }); it('should fail and revert if front-runned', async () => { const preBalance = await weth.balanceOf(arbitrage.address); @@ -195,7 +202,7 @@ describe('Arbitrage', () => { edSignature.v, edSignature.r, edSignature.s, - ZeroEx.toBaseUnitAmount(new BigNumber(1), 18), + amountGet, { from: edFrontRunner }, ); // tslint:disable-next-line:await-promise -- cgit v1.2.3 From 447891b396e4a54e7df426fcbe08dd0d51f4d0f8 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Thu, 1 Mar 2018 13:23:29 -0800 Subject: Move tutorials to adhere to current dir structure --- .../current/tutorials/Arbitrage/Arbitrage.sol | 114 ++++++++++++++ .../current/tutorials/EtherDelta/AccountLevels.sol | 11 ++ .../current/tutorials/EtherDelta/EtherDelta.sol | 168 +++++++++++++++++++++ .../src/current/tutorials/Arbitrage/Arbitrage.sol | 114 -------------- .../current/tutorials/EtherDelta/AccountLevels.sol | 11 -- .../current/tutorials/EtherDelta/EtherDelta.sol | 168 --------------------- 6 files changed, 293 insertions(+), 293 deletions(-) create mode 100644 packages/contracts/src/contracts/current/tutorials/Arbitrage/Arbitrage.sol create mode 100644 packages/contracts/src/contracts/current/tutorials/EtherDelta/AccountLevels.sol create mode 100644 packages/contracts/src/contracts/current/tutorials/EtherDelta/EtherDelta.sol delete mode 100644 packages/contracts/src/current/tutorials/Arbitrage/Arbitrage.sol delete mode 100644 packages/contracts/src/current/tutorials/EtherDelta/AccountLevels.sol delete mode 100644 packages/contracts/src/current/tutorials/EtherDelta/EtherDelta.sol diff --git a/packages/contracts/src/contracts/current/tutorials/Arbitrage/Arbitrage.sol b/packages/contracts/src/contracts/current/tutorials/Arbitrage/Arbitrage.sol new file mode 100644 index 000000000..a9f3c22e6 --- /dev/null +++ b/packages/contracts/src/contracts/current/tutorials/Arbitrage/Arbitrage.sol @@ -0,0 +1,114 @@ +pragma solidity ^0.4.19; + +import { Exchange } from "../../protocol/Exchange/Exchange.sol"; +import { EtherDelta } from "../EtherDelta/EtherDelta.sol"; +import { Ownable } from "../../utils/Ownable/Ownable.sol"; +import { Token } from "../../tokens/Token/Token.sol"; + +/// @title Arbitrage - Facilitates atomic arbitrage of ERC20 tokens between EtherDelta and 0x Exchange contract. +/// @author Leonid Logvinov - +contract Arbitrage is Ownable { + + Exchange exchange; + EtherDelta etherDelta; + address proxyAddress; + + uint256 constant MAX_UINT = 2**256 - 1; + + function Arbitrage(address _exchangeAddress, address _etherDeltaAddress, address _proxyAddress) { + exchange = Exchange(_exchangeAddress); + etherDelta = EtherDelta(_etherDeltaAddress); + proxyAddress = _proxyAddress; + } + + /* + * Makes token tradeable by setting an allowance for etherDelta and 0x proxy contract. + * Also sets an allowance for the owner of the contracts therefore allowing to withdraw tokens. + */ + function setAllowances(address tokenAddress) external onlyOwner { + Token token = Token(tokenAddress); + token.approve(address(etherDelta), MAX_UINT); + token.approve(proxyAddress, MAX_UINT); + token.approve(owner, MAX_UINT); + } + + /* + * Because of the limits on the number of local variables in Solidity we need to compress parameters while loosing + * readability. Scheme of the parameter layout: + * + * addresses + * 0..4 orderAddresses + * 5 user + * + * values + * 0..5 orderValues + * 6 fillTakerTokenAmount + * 7 amountGet + * 8 amountGive + * 9 expires + * 10 nonce + * 11 amount + + * signature + * exchange then etherDelta + */ + function makeAtomicTrade( + address[6] addresses, uint[12] values, + uint8[2] v, bytes32[2] r, bytes32[2] s + ) external onlyOwner { + makeExchangeTrade(addresses, values, v, r, s); + makeEtherDeltaTrade(addresses, values, v, r, s); + } + + function makeEtherDeltaTrade( + address[6] addresses, uint[12] values, + uint8[2] v, bytes32[2] r, bytes32[2] s + ) internal { + uint amount = values[11]; + etherDelta.depositToken( + addresses[2], // tokenGet === makerToken + values[7] // amountGet + ); + etherDelta.trade( + addresses[2], // tokenGet === makerToken + values[7], // amountGet + addresses[3], // tokenGive === takerToken + values[8], // amountGive + values[9], // expires + values[10], // nonce + addresses[5], // user + v[1], + r[1], + s[1], + amount + ); + etherDelta.withdrawToken( + addresses[3], // tokenGive === tokenToken + values[8] // amountGive + ); + } + + function makeExchangeTrade( + address[6] addresses, uint[12] values, + uint8[2] v, bytes32[2] r, bytes32[2] s + ) internal { + address[5] memory orderAddresses = [ + addresses[0], // maker + addresses[1], // taker + addresses[2], // makerToken + addresses[3], // takerToken + addresses[4] // feeRecepient + ]; + uint[6] memory orderValues = [ + values[0], // makerTokenAmount + values[1], // takerTokenAmount + values[2], // makerFee + values[3], // takerFee + values[4], // expirationTimestampInSec + values[5] // salt + ]; + uint fillTakerTokenAmount = values[6]; // fillTakerTokenAmount + // Execute Exchange trade. It either succeeds in full or fails and reverts all the changes. + exchange.fillOrKillOrder(orderAddresses, orderValues, fillTakerTokenAmount, v[0], r[0], s[0]); + } +} diff --git a/packages/contracts/src/contracts/current/tutorials/EtherDelta/AccountLevels.sol b/packages/contracts/src/contracts/current/tutorials/EtherDelta/AccountLevels.sol new file mode 100644 index 000000000..8d7a930d3 --- /dev/null +++ b/packages/contracts/src/contracts/current/tutorials/EtherDelta/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/src/contracts/current/tutorials/EtherDelta/EtherDelta.sol b/packages/contracts/src/contracts/current/tutorials/EtherDelta/EtherDelta.sol new file mode 100644 index 000000000..49847ab48 --- /dev/null +++ b/packages/contracts/src/contracts/current/tutorials/EtherDelta/EtherDelta.sol @@ -0,0 +1,168 @@ +pragma solidity ^0.4.19; + +import { SafeMath } from "../../utils/SafeMath/SafeMath.sol"; +import { AccountLevels } from "./AccountLevels.sol"; +import { Token } from "../../tokens/Token/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 -contract Arbitrage is Ownable { - - Exchange exchange; - EtherDelta etherDelta; - address proxyAddress; - - uint256 constant MAX_UINT = 2**256 - 1; - - function Arbitrage(address _exchangeAddress, address _etherDeltaAddress, address _proxyAddress) { - exchange = Exchange(_exchangeAddress); - etherDelta = EtherDelta(_etherDeltaAddress); - proxyAddress = _proxyAddress; - } - - /* - * Makes token tradeable by setting an allowance for etherDelta and 0x proxy contract. - * Also sets an allowance for the owner of the contracts therefore allowing to withdraw tokens. - */ - function setAllowances(address tokenAddress) external onlyOwner { - Token token = Token(tokenAddress); - token.approve(address(etherDelta), MAX_UINT); - token.approve(proxyAddress, MAX_UINT); - token.approve(owner, MAX_UINT); - } - - /* - * Because of the limits on the number of local variables in Solidity we need to compress parameters while loosing - * readability. Scheme of the parameter layout: - * - * addresses - * 0..4 orderAddresses - * 5 user - * - * values - * 0..5 orderValues - * 6 fillTakerTokenAmount - * 7 amountGet - * 8 amountGive - * 9 expires - * 10 nonce - * 11 amount - - * signature - * exchange then etherDelta - */ - function makeAtomicTrade( - address[6] addresses, uint[12] values, - uint8[2] v, bytes32[2] r, bytes32[2] s - ) external onlyOwner { - makeExchangeTrade(addresses, values, v, r, s); - makeEtherDeltaTrade(addresses, values, v, r, s); - } - - function makeEtherDeltaTrade( - address[6] addresses, uint[12] values, - uint8[2] v, bytes32[2] r, bytes32[2] s - ) internal { - uint amount = values[11]; - etherDelta.depositToken( - addresses[2], // tokenGet === makerToken - values[7] // amountGet - ); - etherDelta.trade( - addresses[2], // tokenGet === makerToken - values[7], // amountGet - addresses[3], // tokenGive === takerToken - values[8], // amountGive - values[9], // expires - values[10], // nonce - addresses[5], // user - v[1], - r[1], - s[1], - amount - ); - etherDelta.withdrawToken( - addresses[3], // tokenGive === tokenToken - values[8] // amountGive - ); - } - - function makeExchangeTrade( - address[6] addresses, uint[12] values, - uint8[2] v, bytes32[2] r, bytes32[2] s - ) internal { - address[5] memory orderAddresses = [ - addresses[0], // maker - addresses[1], // taker - addresses[2], // makerToken - addresses[3], // takerToken - addresses[4] // feeRecepient - ]; - uint[6] memory orderValues = [ - values[0], // makerTokenAmount - values[1], // takerTokenAmount - values[2], // makerFee - values[3], // takerFee - values[4], // expirationTimestampInSec - values[5] // salt - ]; - uint fillTakerTokenAmount = values[6]; // fillTakerTokenAmount - // Execute Exchange trade. It either succeeds in full or fails and reverts all the changes. - exchange.fillOrKillOrder(orderAddresses, orderValues, fillTakerTokenAmount, v[0], r[0], s[0]); - } -} diff --git a/packages/contracts/src/current/tutorials/EtherDelta/AccountLevels.sol b/packages/contracts/src/current/tutorials/EtherDelta/AccountLevels.sol deleted file mode 100644 index 8d7a930d3..000000000 --- a/packages/contracts/src/current/tutorials/EtherDelta/AccountLevels.sol +++ /dev/null @@ -1,11 +0,0 @@ -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/src/current/tutorials/EtherDelta/EtherDelta.sol b/packages/contracts/src/current/tutorials/EtherDelta/EtherDelta.sol deleted file mode 100644 index 49847ab48..000000000 --- a/packages/contracts/src/current/tutorials/EtherDelta/EtherDelta.sol +++ /dev/null @@ -1,168 +0,0 @@ -pragma solidity ^0.4.19; - -import { SafeMath } from "../../utils/SafeMath/SafeMath.sol"; -import { AccountLevels } from "./AccountLevels.sol"; -import { Token } from "../../tokens/Token/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 Date: Thu, 1 Mar 2018 13:24:13 -0800 Subject: Adjust the tests --- packages/contracts/package.json | 4 +- packages/contracts/test/tutorials/arbitrage.ts | 84 ++++++++++++++------------ packages/deployer/src/compiler.ts | 3 - 3 files changed, 49 insertions(+), 42 deletions(-) diff --git a/packages/contracts/package.json b/packages/contracts/package.json index 7cac30069..ca1715d0e 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -17,13 +17,13 @@ "compile:comment": "Yarn workspaces do not link binaries correctly so we need to reference them directly https://github.com/yarnpkg/yarn/issues/3846", "compile": "node ../deployer/lib/src/cli.js compile --contracts ${npm_package_config_contracts} --contracts-dir src/contracts --artifacts-dir src/artifacts", "clean": "shx rm -rf ./lib", - "generate_contract_wrappers": "node ../abi-gen/lib/index.js --abis 'src/artifacts/@(DummyToken|TokenTransferProxy|Exchange|TokenRegistry|MultiSigWallet|MultiSigWalletWithTimeLock|MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress|TokenRegistry|ZRXToken).json' --template ../contract_templates/contract.handlebars --partials '../contract_templates/partials/**/*.handlebars' --output src/contract_wrappers/generated --backend ethers && prettier --write 'src/contract_wrappers/generated/**.ts'", + "generate_contract_wrappers": "node ../abi-gen/lib/index.js --abis 'src/artifacts/@(DummyToken|TokenTransferProxy|Exchange|TokenRegistry|MultiSigWallet|MultiSigWalletWithTimeLock|MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress|TokenRegistry|ZRXToken|Arbitrage|EtherDelta|AccountLevels).json' --template ../contract_templates/contract.handlebars --partials '../contract_templates/partials/**/*.handlebars' --output src/contract_wrappers/generated --backend ethers && prettier --write 'src/contract_wrappers/generated/**.ts'", "migrate": "node ../deployer/lib/src/cli.js migrate", "lint": "tslint --project . 'migrations/**/*.ts' 'test/**/*.ts' 'util/**/*.ts' 'deploy/**/*.ts'", "test:circleci": "yarn test" }, "config": { - "contracts": "Exchange,DummyToken,ZRXToken,Token,WETH9,TokenTransferProxy,MultiSigWallet,MultiSigWalletWithTimeLock,MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress,MaliciousToken,TokenRegistry" + "contracts": "Exchange,DummyToken,ZRXToken,Token,WETH9,TokenTransferProxy,MultiSigWallet,MultiSigWalletWithTimeLock,MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress,MaliciousToken,TokenRegistry,Arbitrage,EtherDelta,AccountLevels" }, "repository": { "type": "git", diff --git a/packages/contracts/test/tutorials/arbitrage.ts b/packages/contracts/test/tutorials/arbitrage.ts index 94589c43a..2bafbff0b 100644 --- a/packages/contracts/test/tutorials/arbitrage.ts +++ b/packages/contracts/test/tutorials/arbitrage.ts @@ -1,4 +1,4 @@ -import { ECSignature, ZeroEx } from '0x.js'; +import { ECSignature, SignedOrder, ZeroEx } from '0x.js'; import { BlockchainLifecycle, devConstants, web3Factory } from '@0xproject/dev-utils'; import { BigNumber } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; @@ -6,11 +6,13 @@ import * as chai from 'chai'; import ethUtil = require('ethereumjs-util'); import * as Web3 from 'web3'; +import { ArbitrageContract } from '../../src/contract_wrappers/generated/arbitrage'; +import { EtherDeltaContract } from '../../src/contract_wrappers/generated/ether_delta'; +import { ExchangeContract } from '../../src/contract_wrappers/generated/exchange'; 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'; @@ -37,10 +39,10 @@ describe('Arbitrage', () => { let weth: Web3.ContractInstance; let zrx: Web3.ContractInstance; - let arbitrage: Web3.ContractInstance; - let etherDelta: Web3.ContractInstance; + let arbitrage: ArbitrageContract; + let etherDelta: EtherDeltaContract; - let order: Order; + let signedOrder: SignedOrder; let exWrapper: ExchangeWrapper; let orderFactory: OrderFactory; @@ -60,7 +62,7 @@ describe('Arbitrage', () => { const edMakerFee = 0; const edTakerFee = 0; const edFeeRebate = 0; - etherDelta = await deployer.deployAsync(ContractName.EtherDelta, [ + const etherDeltaInstance = await deployer.deployAsync(ContractName.EtherDelta, [ edAdminAddress, feeRecipient, accountLevels.address, @@ -68,13 +70,18 @@ describe('Arbitrage', () => { edTakerFee, edFeeRebate, ]); + etherDelta = new EtherDeltaContract(web3Wrapper, etherDeltaInstance.abi, etherDeltaInstance.address); 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] }); + const exchangeInstance = await deployer.deployAsync(ContractName.Exchange, [ + zrx.address, + tokenTransferProxy.address, + ]); + await tokenTransferProxy.addAuthorizedAddress(exchangeInstance.address, { from: accounts[0] }); zeroEx = new ZeroEx(web3.currentProvider, { - exchangeContractAddress: exchange.address, + exchangeContractAddress: exchangeInstance.address, networkId: constants.TESTRPC_NETWORK_ID, }); + const exchange = new ExchangeContract(web3Wrapper, exchangeInstance.abi, exchangeInstance.address); exWrapper = new ExchangeWrapper(exchange, zeroEx); makerTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(1), 18); @@ -83,22 +90,23 @@ describe('Arbitrage', () => { exchangeContractAddress: exchange.address, maker, feeRecipient, - makerToken: zrx.address, - takerToken: weth.address, + makerTokenAddress: zrx.address, + takerTokenAddress: weth.address, makerTokenAmount, takerTokenAmount, makerFee: new BigNumber(0), takerFee: new BigNumber(0), }; - orderFactory = new OrderFactory(web3Wrapper, defaultOrderParams); - arbitrage = await deployer.deployAsync(ContractName.Arbitrage, [ + orderFactory = new OrderFactory(zeroEx, defaultOrderParams); + const arbitrageInstance = await deployer.deployAsync(ContractName.Arbitrage, [ exchange.address, etherDelta.address, tokenTransferProxy.address, ]); + arbitrage = new ArbitrageContract(web3Wrapper, arbitrageInstance.abi, arbitrageInstance.address); // Enable arbitrage and withdrawals of tokens - await arbitrage.setAllowances(weth.address, { from: coinbase }); - await arbitrage.setAllowances(zrx.address, { from: coinbase }); + await arbitrage.setAllowances.sendTransactionAsync(weth.address, { from: coinbase }); + await arbitrage.setAllowances.sendTransactionAsync(zrx.address, { from: coinbase }); // Give some tokens to arbitrage contract await weth.setBalance(arbitrage.address, takerTokenAmount, { from: coinbase }); @@ -114,7 +122,7 @@ describe('Arbitrage', () => { // 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 }); + await etherDelta.depositToken.sendTransactionAsync(weth.address, amountGive, { from: edMaker }); amountGet = makerTokenAmount; // Fund the front runner on EtherDelta side @@ -122,7 +130,7 @@ describe('Arbitrage', () => { // Set the allowance for the front-runner 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 }); + await etherDelta.depositToken.sendTransactionAsync(zrx.address, amountGet, { from: edFrontRunner }); }); beforeEach(async () => { await blockchainLifecycle.startAsync(); @@ -142,7 +150,7 @@ describe('Arbitrage', () => { let nonce: BigNumber; let edSignature: ECSignature; before(async () => { - order = await orderFactory.newSignedOrderAsync(); + signedOrder = await orderFactory.newSignedOrderAsync(); tokenGet = zrx.address; tokenGive = weth.address; const blockNumber = await web3Wrapper.getBlockNumberAsync(); @@ -155,22 +163,22 @@ describe('Arbitrage', () => { const shouldAddPersonalMessagePrefix = false; edSignature = await zeroEx.signOrderHashAsync(edOrderHash, edMaker, shouldAddPersonalMessagePrefix); addresses = [ - order.params.maker, - order.params.taker, - order.params.makerToken, - order.params.takerToken, - order.params.feeRecipient, + signedOrder.maker, + signedOrder.taker, + signedOrder.makerTokenAddress, + signedOrder.takerTokenAddress, + signedOrder.feeRecipient, edMaker, ]; const fillTakerTokenAmount = takerTokenAmount; const edFillAmount = makerTokenAmount; values = [ - order.params.makerTokenAmount, - order.params.takerTokenAmount, - order.params.makerFee, - order.params.takerFee, - order.params.expirationTimestampInSec, - order.params.salt, + signedOrder.makerTokenAmount, + signedOrder.takerTokenAmount, + signedOrder.makerFee, + signedOrder.takerFee, + signedOrder.expirationUnixTimestampSec, + signedOrder.salt, fillTakerTokenAmount, amountGet, amountGive, @@ -178,12 +186,14 @@ describe('Arbitrage', () => { 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]; + v = [signedOrder.ecSignature.v, edSignature.v]; + r = [signedOrder.ecSignature.r, edSignature.r]; + s = [signedOrder.ecSignature.s, edSignature.s]; }); it('should successfully execute the arbitrage if not front-runned', async () => { - const txHash = await arbitrage.makeAtomicTrade(addresses, values, v, r, s, { from: coinbase }); + const txHash = await arbitrage.makeAtomicTrade.sendTransactionAsync(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(amountGive); @@ -191,7 +201,7 @@ describe('Arbitrage', () => { it('should fail and revert if front-runned', async () => { const preBalance = await weth.balanceOf(arbitrage.address); // Front-running transaction - await etherDelta.trade( + await etherDelta.trade.sendTransactionAsync( tokenGet, amountGet, tokenGive, @@ -206,9 +216,9 @@ describe('Arbitrage', () => { { from: edFrontRunner }, ); // tslint:disable-next-line:await-promise - await expect(arbitrage.makeAtomicTrade(addresses, values, v, r, s, { from: coinbase })).to.be.rejectedWith( - constants.REVERT, - ); + await expect( + arbitrage.makeAtomicTrade.sendTransactionAsync(addresses, values, v, r, s, { from: coinbase }), + ).to.be.rejectedWith(constants.REVERT); const postBalance = await weth.balanceOf(arbitrage.address); expect(preBalance).to.be.bignumber.equal(postBalance); }); diff --git a/packages/deployer/src/compiler.ts b/packages/deployer/src/compiler.ts index ea66444b2..149ca5d6d 100644 --- a/packages/deployer/src/compiler.ts +++ b/packages/deployer/src/compiler.ts @@ -209,9 +209,6 @@ 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); -- cgit v1.2.3