Skip to content

Commit

Permalink
Merge pull request #13 from Augmint/exchange-opt
Browse files Browse the repository at this point in the history
Exchange optimization
  • Loading branch information
Peter Petrovics authored Feb 14, 2018
2 parents 6d9cc1c + 45712f1 commit a82d9de
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 44 deletions.
69 changes: 35 additions & 34 deletions contracts/Exchange.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
TODO:
- deduct fee
- consider take funcs (frequent rate changes with takeBuyToken? send more and send back remainder?)
- uint32 for addedTime?
*/
pragma solidity 0.4.19;

Expand All @@ -19,57 +18,58 @@ contract Exchange {
uint public constant CHUNK_SIZE = 100;

struct Order {
uint index;
uint64 index;
address maker;
uint addedTime;

// tokens per ether
uint price;
uint32 price;

// buy order: amount in wei
// sell order: token amount
uint amount;
}

Order[] public buyTokenOrders;
Order[] public sellTokenOrders;
uint64 public orderCount;
mapping(uint64 => Order) public buyTokenOrders;
mapping(uint64 => Order) public sellTokenOrders;

uint[] private activeBuyOrders;
uint[] private activeSellOrders;
uint64[] private activeBuyOrders;
uint64[] private activeSellOrders;

/* used to stop executing matchMultiple when running out of gas.
actual is much less, just leaving enough matchMultipleOrders() to finish TODO: fine tune & test it*/
uint32 private constant ORDER_MATCH_WORST_GAS = 100000;

event NewOrder(uint indexed orderId, address indexed maker, uint price, uint tokenAmount,
event NewOrder(uint64 indexed orderId, address indexed maker, uint32 price, uint tokenAmount,
uint weiAmount);

event OrderFill(address indexed tokenBuyer, address indexed tokenSeller, uint buyTokenOrderId,
uint sellTokenOrderId, uint price, uint weiAmount, uint tokenAmount);
event OrderFill(address indexed tokenBuyer, address indexed tokenSeller, uint64 buyTokenOrderId,
uint64 sellTokenOrderId, uint32 price, uint weiAmount, uint tokenAmount);

event CancelledOrder(uint indexed orderId, address indexed maker, uint tokenAmount, uint weiAmount);
event CancelledOrder(uint64 indexed orderId, address indexed maker, uint tokenAmount, uint weiAmount);

function Exchange(AugmintTokenInterface _augmintToken) public {
augmintToken = _augmintToken;
}

function placeBuyTokenOrder(uint price) external payable returns (uint orderId) {
function placeBuyTokenOrder(uint32 price) external payable returns (uint64 orderId) {
require(price > 0);
require(msg.value > 0);

orderId = buyTokenOrders.push(Order(activeBuyOrders.length, msg.sender, now, price, msg.value)) - 1;
orderId = ++orderCount;
buyTokenOrders[orderId] = Order(uint64(activeBuyOrders.length), msg.sender, price, msg.value);
activeBuyOrders.push(orderId);

NewOrder(orderId, msg.sender, price, 0, msg.value);
}

/* this function requires previous approval to transfer tokens */
function placeSellTokenOrder(uint price, uint tokenAmount) external returns (uint orderId) {
function placeSellTokenOrder(uint32 price, uint tokenAmount) external returns (uint orderId) {
augmintToken.transferFrom(msg.sender, this, tokenAmount);
return _placeSellTokenOrder(msg.sender, price, tokenAmount);
}

function cancelBuyTokenOrder(uint buyTokenId) external {
function cancelBuyTokenOrder(uint64 buyTokenId) external {
Order storage order = buyTokenOrders[buyTokenId];
require(order.maker == msg.sender);

Expand All @@ -82,7 +82,7 @@ contract Exchange {
CancelledOrder(buyTokenId, msg.sender, 0, amount);
}

function cancelSellTokenOrder(uint sellTokenId) external {
function cancelSellTokenOrder(uint64 sellTokenId) external {
Order storage order = sellTokenOrders[sellTokenId];
require(order.maker == msg.sender);

Expand All @@ -99,15 +99,15 @@ contract Exchange {
trade price meets in the middle
reverts if any of the orders have been removed
*/
function matchOrders(uint buyTokenId, uint sellTokenId) external {
function matchOrders(uint64 buyTokenId, uint64 sellTokenId) external {
_fillOrder(buyTokenId, sellTokenId);
}

/* matches as many orders as possible from the passed orders
Runs as long as gas is available for the call.
Stops if any match is invalid (case when any of the orders removed after client generated the match list sent)
*/
function matchMultipleOrders(uint[] buyTokenIds, uint[] sellTokenIds) external returns(uint matchCount) {
function matchMultipleOrders(uint64[] buyTokenIds, uint64[] sellTokenIds) external returns(uint matchCount) {
uint len = buyTokenIds.length;
require(len == sellTokenIds.length);
for (uint i = 0; i < len && msg.gas > ORDER_MATCH_WORST_GAS; i++) {
Expand All @@ -121,20 +121,20 @@ contract Exchange {
}

// returns CHUNK_SIZE orders starting from offset
// orders are encoded as [id, maker, addedTime, price, amount]
function getActiveBuyOrders(uint offset) external view returns (uint[5][CHUNK_SIZE] response) {
// orders are encoded as [id, maker, price, amount]
function getActiveBuyOrders(uint offset) external view returns (uint[4][CHUNK_SIZE] response) {
for (uint8 i = 0; i < CHUNK_SIZE && i + offset < activeBuyOrders.length; i++) {
uint orderId = activeBuyOrders[offset + i];
uint64 orderId = activeBuyOrders[offset + i];
Order storage order = buyTokenOrders[orderId];
response[i] = [orderId, uint(order.maker), order.addedTime, order.price, order.amount];
response[i] = [orderId, uint(order.maker), order.price, order.amount];
}
}

function getActiveSellOrders(uint offset) external view returns (uint[5][CHUNK_SIZE] response) {
function getActiveSellOrders(uint offset) external view returns (uint[4][CHUNK_SIZE] response) {
for (uint8 i = 0; i < CHUNK_SIZE && i + offset < activeSellOrders.length; i++) {
uint orderId = activeSellOrders[offset + i];
uint64 orderId = activeSellOrders[offset + i];
Order storage order = sellTokenOrders[orderId];
response[i] = [orderId, uint(order.maker), order.addedTime, order.price, order.amount];
response[i] = [orderId, uint(order.maker), order.price, order.amount];
}
}

Expand All @@ -146,17 +146,17 @@ contract Exchange {
*/
function transferNotification(address maker, uint tokenAmount, uint price) public {
require(msg.sender == address(augmintToken));
_placeSellTokenOrder(maker, price, tokenAmount);
_placeSellTokenOrder(maker, uint32(price), tokenAmount);
}

function _fillOrder(uint buyTokenId, uint sellTokenId) private {
function _fillOrder(uint64 buyTokenId, uint64 sellTokenId) private {
Order storage buy = buyTokenOrders[buyTokenId];
Order storage sell = sellTokenOrders[sellTokenId];

require(buy.price >= sell.price);

// meet in the middle
uint price = buy.price.add(sell.price).div(2);
uint price = uint(buy.price).add(sell.price).div(2);

uint sellWei = sell.amount.mul(1 ether).div(price);

Expand Down Expand Up @@ -184,16 +184,17 @@ contract Exchange {
sell.maker.transfer(tradedWei);

OrderFill(buy.maker, sell.maker, buyTokenId,
sellTokenId, price, tradedWei, tradedTokens);
sellTokenId, uint32(price), tradedWei, tradedTokens);
}


function _placeSellTokenOrder(address maker, uint price, uint tokenAmount)
private returns (uint orderId) {
function _placeSellTokenOrder(address maker, uint32 price, uint tokenAmount)
private returns (uint64 orderId) {
require(price > 0);
require(tokenAmount > 0);

orderId = sellTokenOrders.push(Order(activeSellOrders.length, maker, now, price, tokenAmount)) - 1;
orderId = ++orderCount;
sellTokenOrders[orderId] = Order(uint64(activeSellOrders.length), maker, price, tokenAmount);
activeSellOrders.push(orderId);

NewOrder(orderId, maker, price, tokenAmount, 0);
Expand All @@ -207,7 +208,7 @@ contract Exchange {
_removeOrder(activeSellOrders, order.index);
}

function _removeOrder(uint[] storage orders, uint index) private {
function _removeOrder(uint64[] storage orders, uint64 index) private {
if (index < orders.length - 1) {
orders[index] = orders[orders.length - 1];
}
Expand Down
15 changes: 5 additions & 10 deletions test/helpers/exchangeTestHelpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ async function newOrder(testInstance, order) {
assert.equal(state.sellCount, expSellCount, "sellCount should be set");
assert.equal(actualOrder.id, order.id, "orderId should be set in contract's order array");
assert.equal(actualOrder.maker, order.maker, "maker should be the userAccount in contract's order array");
// TODO: assert order.addedTime
assert.equal(actualOrder.price, order.price, "price should be set in contract's order array");
assert.equal(
actualOrder.amount.toString(),
Expand Down Expand Up @@ -327,21 +326,19 @@ function parseOrder(order) {
return {
index: order[0],
maker: order[1],
addedTime: order[2].toNumber(),
price: order[3].toNumber(),
amount: order[4]
price: order[2].toNumber(),
amount: order[3]
};
}

function parseOrders(orderType, orders) {
return orders.filter(order => order[4].toNumber() != 0).map(function(order) {
return orders.filter(order => order[3].toNumber() != 0).map(function(order) {
return {
orderType: orderType,
id: order[0].toNumber(),
maker: "0x" + order[1].toString(16),
addedTime: order[2].toNumber(),
price: order[3].toNumber(),
amount: order[4]
price: order[2].toNumber(),
amount: order[3]
};
});
}
Expand Down Expand Up @@ -375,7 +372,6 @@ async function printOrderBook(_limit) {
const order = await getSellTokenOrder(i);
console.log(
`SELL token: ACE/ETH: ${order.price / 10000} amount: ${order.amount.toString() / 10000} ACE` +
` ${moment.unix(order.addedTime).format("HH:mm:ss")}` +
` orderIdx: ${i} orderId: ${order.id} acc: ${order.maker}`
);
}
Expand All @@ -384,7 +380,6 @@ async function printOrderBook(_limit) {
const order = await getBuyTokenOrder(i);
console.log(
` BUY token: ACE/EUR: ${order.price / 10000} amount: ${web3.fromWei(order.amount)} ETH` +
` ${moment.unix(order.addedTime).format("HH:mm:ss")}` +
` orderIdx: ${i} orderId: ${order.id} acc: ${order.maker}`
);
}
Expand Down

0 comments on commit a82d9de

Please sign in to comment.