Skip to content

Commit

Permalink
Merge pull request #88 from Augmint/staging
Browse files Browse the repository at this point in the history
* chore(package): update rollup to version 1.12.4
* publish API documentation (#83)
* add toNumber to Wei (#85)
* Market order matching estimates (#79)
* Bump fstream from 1.0.11 to 1.0.12 (#86)
  • Loading branch information
phraktle authored Jun 17, 2019
2 parents 0ab2656 + 97f8ef4 commit 9628606
Show file tree
Hide file tree
Showing 9 changed files with 380 additions and 39 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<span style="display:block;text-align:center">![Augmint](http://www.augmint.cc/android-chrome-192x192.png)
<span style="display:block;text-align:center">![Augmint](https://www.augmint.org/android-chrome-192x192.png)
</span>

# Augmint - Stable Digital Tokens - Javascript Library (WIP)
Expand Down
22 changes: 22 additions & 0 deletions generateDocs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/bin/sh

cat >./dist/sourcefile-map.json <<EOF
[
{
"pattern": "^",
"replace": "${REPOSITORY_URL}/tree/${BRANCH}/src/"
}
]
EOF

typedoc \
--out ./dist/docs ./src \
--mode modules \
--target ES6 \
--tsconfig ./tsconfig.json \
--exclude node_modules \
--ignoreCompilerErrors \
--excludeExternals \
--excludePrivate \
--excludeNotExported \
--sourcefile-url-map ./dist/sourcefile-map.json
13 changes: 9 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@augmint/js",
"version": "0.3.2",
"version": "0.3.3",
"description": "Augmint Javascript Library",
"keywords": [
"augmint javascript library A-EUR stablecoin web3 dapp"
Expand Down Expand Up @@ -41,14 +41,16 @@
"cross-env": "5.2",
"eslint": "5.16.0",
"mocha": "6.1.4",
"rollup": "1.12.3",
"rollup": "1.12.4",
"rollup-plugin-node-builtins": "2.1.2",
"rollup-plugin-node-globals": "1.4.0",
"rollup-plugin-typescript2": "0.21.1",
"sinon": "7.3.2",
"tslint": "5.16.0",
"tslint-config-prettier": "1.18.0",
"typechain": "0.3.14",
"typedoc": "0.14.2",
"typedoc-plugin-sourcefile-url": "1.0.4",
"typescript": "3.4.3",
"wait-on": "3.2.0"
},
Expand All @@ -59,10 +61,13 @@
"test": "yarn build && yarn cross-env NODE_ENV=test NODE_PATH=./ mocha 'test/**/*.test.js' --exit --timeout 5000",
"ganache:start": "scripts/augmint-cli.sh ganache start --blockTime 1",
"ganache:stop": "scripts/augmint-cli.sh ganache stop",
"ganache:run": "scripts/augmint-cli.sh ganache run --blockTime 1"
"ganache:run": "scripts/augmint-cli.sh ganache run --blockTime 1",
"typedoc": "./generateDocs.sh"
},
"greenkeeper": {
"ignore": ["@types/node"]
"ignore": [
"@types/node"
]
},
"main": "dist/index.js",
"module": "dist/index.es.js",
Expand Down
65 changes: 63 additions & 2 deletions src/Exchange.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ import { Rates } from "./Rates";
import { Transaction } from "./Transaction";
import { Ratio, Tokens, Wei } from "./units";

interface IMarketMatch {
filledTokens: Tokens;
filledEthers: Wei;
limitPrice?: Ratio;
averagePrice?: Ratio;
}

export class OrderBook {

public static compareBuyOrders(o1: IBuyOrder, o2: IBuyOrder): number {
Expand All @@ -22,6 +29,35 @@ export class OrderBook {
return cmp !== 0 ? cmp : o1.id - o2.id;
}

private static estimateMarketOrder<T extends IOrder>(orders: T[],
tokens: Tokens,
rate: Tokens,
toTokens: (order: T) => Tokens): IMarketMatch {
const ret: IMarketMatch = {
filledTokens: Tokens.of(0),
filledEthers: Wei.of(0)
};

for (const order of orders) {
const remaining = tokens.sub(ret.filledTokens);
if (remaining.isZero()) {
break;
}

const fillTokens: Tokens = Tokens.min(toTokens(order), remaining);
const fillEthers: Wei = fillTokens.toWeiAt(rate, order.price);

ret.filledTokens = ret.filledTokens.add(fillTokens);
ret.filledEthers = ret.filledEthers.add(fillEthers);
ret.limitPrice = order.price;
}
if (ret.limitPrice) {
ret.averagePrice = rate.divToRatio(ret.filledTokens.toRate(ret.filledEthers));
}

return ret;
}

constructor(public buyOrders: IBuyOrder[], public sellOrders: ISellOrder[]) {
buyOrders.sort(OrderBook.compareBuyOrders);
sellOrders.sort(OrderBook.compareSellOrders);
Expand All @@ -47,13 +83,16 @@ export class OrderBook {
if (!this.hasMatchingOrders()) {
return { buyIds, sellIds, gasEstimate: 0 };
}

const lowestSellPrice: Ratio = this.sellOrders[0].price;
const highestBuyPrice: Ratio = this.buyOrders[0].price;

const clone = o => Object.assign({}, o);
const buys: IBuyOrder[] = this.buyOrders.filter(o => o.price.gte(lowestSellPrice)).map(clone);
const buys: IBuyOrder[] = this.buyOrders
.filter(o => o.price.gte(lowestSellPrice)).map(clone);

const sells: ISellOrder[] = this.sellOrders.filter(o => o.price.lte(highestBuyPrice)).map(clone);
const sells: ISellOrder[] = this.sellOrders
.filter(o => o.price.lte(highestBuyPrice)).map(clone);

let buyIdx: number = 0;
let sellIdx: number = 0;
Expand Down Expand Up @@ -98,6 +137,28 @@ export class OrderBook {

return { buyIds, sellIds, gasEstimate };
}

/**
* calculate price for n amount of token to buy
* @return {object} simple buy data { tokens, ethers, limitPrice, averagePrice }
* @param tokenAmount - amount of token to buy
* @param ethFiatRate - current rate
*/
public estimateMarketBuy(tokenAmount: Tokens, ethFiatRate: Tokens): IMarketMatch {
return OrderBook.estimateMarketOrder(this.sellOrders, tokenAmount, ethFiatRate,
order => order.amount);
}

/**
* calculate price for n amount of token to sell
* @return {object} simple buy data { tokens, ethers, limitPrice, averagePrice }
* @param tokenAmount - amount of token to sell
* @param ethFiatRate - current rate
*/
public estimateMarketSell(tokenAmount: Tokens, ethFiatRate: Tokens): IMarketMatch {
return OrderBook.estimateMarketOrder(this.buyOrders, tokenAmount, ethFiatRate,
order => order.amount.toTokensAt(ethFiatRate, order.price));
}
}

export interface IMatchingOrders {
Expand Down
10 changes: 8 additions & 2 deletions src/units.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ abstract class FixedPoint {
return this.create(ceilDiv(this.amount.mul(Ratio.DIV_BN), ratio.amount));
}

public divToRatio(other: this): Ratio {
this.check(other);
return new Ratio(this.amount.mul(Ratio.DIV_BN).divRound(other.amount));
}

//
// comparison
//
Expand Down Expand Up @@ -125,6 +130,9 @@ export class Wei extends FixedPoint {
return new Tokens(this.amount.mul(rate.amount).divRound(price.amount.mul(E12)));
}

public toNumber(): number {
return this.amount.toNumber() / Wei.DIV_BN.toNumber()
}
}

export class Tokens extends FixedPoint {
Expand Down Expand Up @@ -158,7 +166,6 @@ export class Tokens extends FixedPoint {
this.check(ethers, Wei);
return new Tokens(this.amount.mul(Wei.DIV_BN).divRound(ethers.amount));
}

}


Expand All @@ -178,6 +185,5 @@ export class Ratio extends FixedPoint {
public toNumber(): number {
return this.amount.toNumber() / Ratio.DIV;
}

}

65 changes: 65 additions & 0 deletions test/Exchange.simpleBuy.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
const { assert } = require("chai");

const { normalizeBN } = require("./testHelpers/normalize.js");
const { Augmint, utils, Wei, Tokens, Ratio } = require("../dist/index.js");
const loadEnv = require("./testHelpers/loadEnv.js");
const OrderBook = Augmint.Exchange.OrderBook;

const config = loadEnv();

if (config.LOG) {
utils.logger.level = config.LOG;
}

const RATE = Tokens.of(400.00);

describe("calculate market orders", () => {
it("should return matching info", () => {
const buyOrders = [
{ amount: Wei.of(0.0070), price: Ratio.of(1.2) },
{ amount: Wei.of(0.0094), price: Ratio.of(1) },
{ amount: Wei.of(0.0064), price: Ratio.of(0.7) }
];

const sellOrders = [
{ amount: Tokens.of(1), price: Ratio.of(1.02) },
{ amount: Tokens.of(10), price: Ratio.of(1.03) }
];

const orderBook = new OrderBook(buyOrders, sellOrders);

const sellResult = {
filledTokens: Tokens.of(6),
filledEthers: Wei.of(0.016165),
limitPrice: Ratio.of(1),
averagePrice: Ratio.of(1.077673)
};

const sellMatches = orderBook.estimateMarketSell(Tokens.of(6), RATE);

assert.deepEqual(normalizeBN(sellResult), normalizeBN(sellMatches));

const buyResult = {
filledTokens: Tokens.of(2),
filledEthers: Wei.of(0.005125),
limitPrice: Ratio.of(1.03),
averagePrice: Ratio.of(1.02501)
};

const buyMatches = orderBook.estimateMarketBuy(Tokens.of(2), RATE);

assert.deepEqual(normalizeBN(buyMatches), normalizeBN(buyResult));
});

it("should work on empty order book", () => {
const orderBook = new OrderBook([], []);
const exp = {
filledTokens: Tokens.of(0),
filledEthers: Wei.of(0)
};
const buyMatches = orderBook.estimateMarketBuy(Tokens.of(100), RATE);
const sellMatches = orderBook.estimateMarketSell(Tokens.of(100), RATE)
assert.deepEqual(normalizeBN(buyMatches), normalizeBN(exp));
assert.deepEqual(normalizeBN(sellMatches), normalizeBN(exp));
});
});
20 changes: 1 addition & 19 deletions test/LoanManager.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@ const chai = require("chai");
const { assert, expect } = chai;
chai.use(require("chai-exclude"));

const BN = require("bn.js");
BN.prototype.inspect = function() {
return this.toString();
};

const { normalizeBN } = require("./testHelpers/normalize.js");
const { takeSnapshot, revertSnapshot } = require("./testHelpers/ganache.js");
const { Augmint, utils, Wei, Tokens, Ratio } = require("../dist/index.js");
const { AugmintJsError } = Augmint.Errors;
Expand All @@ -17,20 +13,6 @@ if (config.LOG) {
utils.logger.level = config.LOG;
}

// deeply normalize all BN properties so they can be compared with deepEquals
// NOTE: object graph must not have cycles
function normalizeBN(obj) {
Object.keys(obj).map((key, index) => {
const o = obj[key];
if (o instanceof BN) {
obj[key] = new BN(o.toString());
} else if (o instanceof Object) {
obj[key] = normalizeBN(o);
}
});
return obj;
}

function mockProd(
id,
minDisbursedAmount,
Expand Down
21 changes: 21 additions & 0 deletions test/testHelpers/normalize.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const BN = require("bn.js");

BN.prototype.inspect = function() {
return this.toString();
};

module.exports = { normalizeBN };

// deeply normalize all BN properties so they can be compared with deepEquals
// NOTE: object graph must not have cycles
function normalizeBN(obj) {
Object.keys(obj).map((key, index) => {
const o = obj[key];
if (o instanceof BN) {
obj[key] = new BN(o.toString());
} else if (o instanceof Object) {
obj[key] = normalizeBN(o);
}
});
return obj;
}
Loading

0 comments on commit 9628606

Please sign in to comment.