Skip to content

Commit

Permalink
Merge pull request #86 from Augmint/published_rate_order
Browse files Browse the repository at this point in the history
New exchange: published rate orders
  • Loading branch information
Peter Petrovics authored May 3, 2018
2 parents 0cb8d8d + 2c3844d commit 04a7819
Show file tree
Hide file tree
Showing 10 changed files with 836 additions and 230 deletions.
595 changes: 595 additions & 0 deletions abiniser/abis/Exchange_ABI_3c157a5256a2093da587f166d4dbd537.json

Large diffs are not rendered by default.

20 changes: 19 additions & 1 deletion abiniser/deployments/4/Exchange_DEPLOYS.json

Large diffs are not rendered by default.

187 changes: 97 additions & 90 deletions abiniser/deployments/999/Exchange_DEPLOYS.json

Large diffs are not rendered by default.

24 changes: 13 additions & 11 deletions contracts/Exchange.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ contract Exchange is Restricted {
uint64 index;
address maker;

// tokens per ether for limit orders. 0 when order is on current published peggedSymbol/ETH rates
// % of published current peggedSymbol/ETH rates published by Rates contract. Stored as parts per million
// I.e. 1,000,000 = 100% (parity), 990,000 = 1% below parity
uint32 price;

// buy order: amount in wei
Expand All @@ -50,7 +51,7 @@ contract Exchange is Restricted {
event NewOrder(uint64 indexed orderId, address indexed maker, uint32 price, uint tokenAmount, uint weiAmount);

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

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

Expand All @@ -69,6 +70,7 @@ contract Exchange is Restricted {
}

function placeBuyTokenOrder(uint32 price) external payable returns (uint64 orderId) {
require(price > 0, "price must be > 0");
require(msg.value > 0, "msg.value must be > 0");

orderId = ++orderCount;
Expand Down Expand Up @@ -169,17 +171,16 @@ contract Exchange is Restricted {
Order storage buy = buyTokenOrders[buyTokenId];
Order storage sell = sellTokenOrders[sellTokenId];

require(buy.price >= sell.price || buy.price == 0, "buy price must be >= sell price or sell or buy price == 0");
require(buy.price >= sell.price, "buy price must be >= sell price");

// pick maker's price (whoever placed order sooner considered as maker)
uint32 price = buyTokenId > sellTokenId ? sell.price : buy.price;

uint publishedRate;
(publishedRate, ) = rates.rates(augmintToken.peggedSymbol());
uint buyPrice = buy.price > 0 ? buy.price : publishedRate;
uint sellPrice = sell.price > 0 ? sell.price : publishedRate;

// pick maker's price (whoever placed order sooner considered as maker)
uint price = buyTokenId > sellTokenId ? sellPrice : buyPrice;
uint fillRate = publishedRate.mul(price).roundedDiv(1000000);

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

uint tradedWei;
uint tradedTokens;
Expand All @@ -188,7 +189,7 @@ contract Exchange is Restricted {
tradedTokens = sell.amount;
} else {
tradedWei = buy.amount;
tradedTokens = buy.amount.mul(price).roundedDiv(1 ether);
tradedTokens = buy.amount.mul(fillRate).roundedDiv(1 ether);
}

buy.amount = buy.amount.sub(tradedWei);
Expand All @@ -205,11 +206,12 @@ contract Exchange is Restricted {
sell.maker.transfer(tradedWei);

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

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

orderId = ++orderCount;
Expand Down
8 changes: 4 additions & 4 deletions migrations/97_add_legacyExchange.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ module.exports = async function(deployer, network, accounts) {
]);

await Promise.all([
tokenAEur.transferAndNotify(oldExchange.address, 2000, 0),
tokenAEur.transferAndNotify(oldExchange.address, 1100, 997),
oldExchange.placeBuyTokenOrder(999, { value: web3.toWei(0.01) }),
oldExchange.placeBuyTokenOrder(0, { value: web3.toWei(0.011) })
tokenAEur.transferAndNotify(oldExchange.address, 2000, 1010000),
tokenAEur.transferAndNotify(oldExchange.address, 1100, 980000),
oldExchange.placeBuyTokenOrder(990000, { value: web3.toWei(0.01) }),
oldExchange.placeBuyTokenOrder(1020000, { value: web3.toWei(0.011) })
]);

console.log(
Expand Down
49 changes: 49 additions & 0 deletions rinkeby_migrations/16_deploy_new_Exchange.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/* Deploy new Exchange with publish rate orders */
const Migrations = artifacts.require("./Migrations.sol");
const Exchange = artifacts.require("./Exchange.sol");
const FeeAccount = artifacts.require("./FeeAccount.sol");

module.exports = function(deployer, network, accounts) {
let tokenAEurAddress;
let feeAccountAddress;
let ratesAddress;
let monetaryBoardAccounts;

if (network === "rinkeby" || network === "rinkeby-fork") {
// Truffle artifacts are in unknown state when truffle migrate starts from this step on rinkeby.
// Therefore we can't use addresses from there.
// But this script will be run only ONCE on rinkeby so it's fine to hardcode addresses
tokenAEurAddress = "0x135893f1a6b3037bb45182841f18f69327366992";
feeAccountAddress = "0xc70b65e40f877cdc6d8d2ebfd44d63efbeb7fc6d";
ratesAddress = "0xcA8100FCcb479516A5b30f8Bc5dAeA09Fb7a7473";

monetaryBoardAccounts = [
accounts[0],
"0x14A9dc091053fCbA9474c5734078238ff9904364" /* Krosza */,
"0xe71E9636e31B838aF0A3c38B3f3449cdC2b7aa87" /* Phraktle */
];

const feeAccount = FeeAccount.at(feeAccountAddress);

deployer.deploy(Exchange, tokenAEurAddress, ratesAddress);
deployer.then(async () => {
console.log("Deployed Exchange at ", Exchange.address);
const newExchange = Exchange.at(Exchange.address);
const grantMonetaryBoardTxs = monetaryBoardAccounts.map(acc =>
newExchange.grantPermission(acc, "MonetaryBoard")
);

await Promise.all([
grantMonetaryBoardTxs,
feeAccount.grantPermission(Exchange.address, "NoFeeTransferContracts")
]);

// update truffle Migrations step manually
await Migrations.at("0xb96f7e79a6b3faf4162e274ff764ca9de598b0c5").setCompleted(16);
});
} else {
// Not rinkeby, we assume it's a private network: scripts in this folder are not intended to run on other networks!
// we don't need to do anything as previous steps deployed latest version of Exchange
console.log("On ", network, "not Rinkeby. Not executing anything in step 16.");
}
};
72 changes: 15 additions & 57 deletions test/exchangeMatching.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ contract("Exchange matching tests", accounts => {
});

it("should match two matching orders (buy token fully filled)", async function() {
const buyOrder = { amount: web3.toWei(0.535367), maker: maker, price: 110000, orderType: TOKEN_BUY };
const sellOrder = { amount: 95582, maker: taker, price: 90000, orderType: TOKEN_SELL };
const buyOrder = { amount: web3.toWei(0.535367), maker: maker, price: 1010000, orderType: TOKEN_BUY };
const sellOrder = { amount: 95582, maker: taker, price: 990000, orderType: TOKEN_SELL };

await exchangeTestHelper.newOrder(this, buyOrder);
await exchangeTestHelper.newOrder(this, sellOrder);
Expand All @@ -43,8 +43,8 @@ contract("Exchange matching tests", accounts => {
});

it("should match two matching orders (sell token fully filled)", async function() {
const buyOrder = { amount: web3.toWei(1.7504), maker: maker, price: 110000, orderType: TOKEN_BUY };
const sellOrder = { amount: 56141, maker: taker, price: 90000, orderType: TOKEN_SELL };
const buyOrder = { amount: web3.toWei(1.7504), maker: maker, price: 1010000, orderType: TOKEN_BUY };
const sellOrder = { amount: 56141, maker: taker, price: 990000, orderType: TOKEN_SELL };

await exchangeTestHelper.newOrder(this, buyOrder);
await exchangeTestHelper.newOrder(this, sellOrder);
Expand All @@ -57,8 +57,8 @@ contract("Exchange matching tests", accounts => {
});

it("should match two matching orders (both fully filled)", async function() {
const buyOrder = { amount: web3.toWei(1), maker: maker, price: 100000, orderType: TOKEN_BUY };
const sellOrder = { amount: 100000, maker: maker, price: 90000, orderType: TOKEN_SELL };
const buyOrder = { amount: web3.toWei(1), maker: maker, price: 1000000, orderType: TOKEN_BUY };
const sellOrder = { amount: 99800, maker: maker, price: 990000, orderType: TOKEN_SELL };

await exchangeTestHelper.newOrder(this, buyOrder);
await exchangeTestHelper.newOrder(this, sellOrder);
Expand All @@ -70,55 +70,13 @@ contract("Exchange matching tests", accounts => {
assert.equal(stateAfter.buyCount, 0, "Buy token order count should be 0");
});

it("should match a limit sell and published rate buy type order", async function() {
const buyOrder = { amount: web3.toWei(1), maker: maker, price: 0, orderType: TOKEN_BUY };
const sellOrder = { amount: 100000, maker: maker, price: 90000, orderType: TOKEN_SELL };

await exchangeTestHelper.newOrder(this, buyOrder);
await exchangeTestHelper.newOrder(this, sellOrder);
//await exchangeTestHelper.printOrderBook(10);
await exchangeTestHelper.matchOrders(this, buyOrder, sellOrder);

const stateAfter = await exchangeTestHelper.getState();
assert.equal(stateAfter.sellCount, 1, "Sell token order count should be 1");
assert.equal(stateAfter.buyCount, 0, "Buy token order count should be 0");
});

it("should match a limit buy and published rate sell type order", async function() {
const buyOrder = { amount: web3.toWei(1), maker: maker, price: 90000, orderType: TOKEN_BUY };
const sellOrder = { amount: 100000, maker: maker, price: 0, orderType: TOKEN_SELL };

await exchangeTestHelper.newOrder(this, buyOrder);
await exchangeTestHelper.newOrder(this, sellOrder);
//await exchangeTestHelper.printOrderBook(10);
await exchangeTestHelper.matchOrders(this, buyOrder, sellOrder);

const stateAfter = await exchangeTestHelper.getState();
assert.equal(stateAfter.sellCount, 1, "Sell token order count should be 1");
assert.equal(stateAfter.buyCount, 0, "Buy token order count should be 0");
});

it("should match two published rate sell type orders", async function() {
const buyOrder = { amount: web3.toWei(1), maker: maker, price: 0, orderType: TOKEN_BUY };
const sellOrder = { amount: 100000, maker: maker, price: 0, orderType: TOKEN_SELL };

await exchangeTestHelper.newOrder(this, buyOrder);
await exchangeTestHelper.newOrder(this, sellOrder);
//await exchangeTestHelper.printOrderBook(10);
await exchangeTestHelper.matchOrders(this, buyOrder, sellOrder);

const stateAfter = await exchangeTestHelper.getState();
assert.equal(stateAfter.sellCount, 1, "Sell token order count should be 1");
assert.equal(stateAfter.buyCount, 0, "Buy token order count should be 0");
});

it("should fully fill both orders when buy token amount expected to be same as sell token amount", async function() {
/* from users perspective:
Sell: 100A€ / 998 A€/ETH = 0.1002004008 ETH
Buy: 0.1002004008 ETH * 998 A€/ETH = 99.9999999984 A€ wich is 100A€ b/c A€ is w/ 2 decimals
*/
const buyOrder = { amount: web3.toWei(0.1002004008), maker: maker, price: 99800, orderType: TOKEN_BUY };
const sellOrder = { amount: 10000, maker: maker, price: 99800, orderType: TOKEN_SELL };
const buyOrder = { amount: web3.toWei(0.1002004008), maker: maker, price: 1000000, orderType: TOKEN_BUY };
const sellOrder = { amount: 10000, maker: maker, price: 1000000, orderType: TOKEN_SELL };

await exchangeTestHelper.newOrder(this, buyOrder);
await exchangeTestHelper.newOrder(this, sellOrder);
Expand All @@ -133,8 +91,8 @@ contract("Exchange matching tests", accounts => {
});

it("should match two matching orders from the same account on sell price if placed first ", async function() {
const buyOrder = { amount: web3.toWei(1.7504), maker: maker, price: 110000, orderType: TOKEN_BUY };
const sellOrder = { amount: 56141, maker: maker, price: 90000, orderType: TOKEN_SELL };
const buyOrder = { amount: web3.toWei(1.7504), maker: maker, price: 1010000, orderType: TOKEN_BUY };
const sellOrder = { amount: 56141, maker: maker, price: 990000, orderType: TOKEN_SELL };

await exchangeTestHelper.newOrder(this, sellOrder);
await exchangeTestHelper.newOrder(this, buyOrder);
Expand All @@ -148,18 +106,18 @@ contract("Exchange matching tests", accounts => {

it("should NOT match two non-matching orders", async function() {
// buy price lower then sell price, should fail
const buyOrder = { amount: web3.toWei(1.7504), maker: maker, price: 110000, orderType: TOKEN_BUY };
const sellOrder = { amount: 56141, maker: maker, price: 115000, orderType: TOKEN_SELL };
const buyOrder = { amount: web3.toWei(1.7504), maker: maker, price: 1010000, orderType: TOKEN_BUY };
const sellOrder = { amount: 56141, maker: maker, price: 1010001, orderType: TOKEN_SELL };

await exchangeTestHelper.newOrder(this, buyOrder);
await exchangeTestHelper.newOrder(this, sellOrder);
await testHelpers.expectThrow(exchange.matchOrders(buyOrder.id, sellOrder.id));
});

it("shouldn't matchmultiple if different count of buy&sell orders passed ", async function() {
const buyOrder1 = { amount: web3.toWei(0.535367), maker: maker, price: 110000, orderType: TOKEN_BUY };
const sellOrder1 = { amount: 95582, maker: taker, price: 90000, orderType: TOKEN_SELL };
const sellOrder2 = { amount: 95582, maker: taker, price: 90000, orderType: TOKEN_SELL };
const buyOrder1 = { amount: web3.toWei(0.535367), maker: maker, price: 1010000, orderType: TOKEN_BUY };
const sellOrder1 = { amount: 95582, maker: taker, price: 900000, orderType: TOKEN_SELL };
const sellOrder2 = { amount: 95582, maker: taker, price: 900000, orderType: TOKEN_SELL };

await exchangeTestHelper.newOrder(this, buyOrder1);
await exchangeTestHelper.newOrder(this, sellOrder1);
Expand Down
Loading

0 comments on commit 04a7819

Please sign in to comment.