Skip to content

Commit 9628606

Browse files
authored
Merge pull request #88 from Augmint/staging
* 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)
2 parents 0ab2656 + 97f8ef4 commit 9628606

9 files changed

+380
-39
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<span style="display:block;text-align:center">![Augmint](http://www.augmint.cc/android-chrome-192x192.png)
1+
<span style="display:block;text-align:center">![Augmint](https://www.augmint.org/android-chrome-192x192.png)
22
</span>
33

44
# Augmint - Stable Digital Tokens - Javascript Library (WIP)

generateDocs.sh

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#!/bin/sh
2+
3+
cat >./dist/sourcefile-map.json <<EOF
4+
[
5+
{
6+
"pattern": "^",
7+
"replace": "${REPOSITORY_URL}/tree/${BRANCH}/src/"
8+
}
9+
]
10+
EOF
11+
12+
typedoc \
13+
--out ./dist/docs ./src \
14+
--mode modules \
15+
--target ES6 \
16+
--tsconfig ./tsconfig.json \
17+
--exclude node_modules \
18+
--ignoreCompilerErrors \
19+
--excludeExternals \
20+
--excludePrivate \
21+
--excludeNotExported \
22+
--sourcefile-url-map ./dist/sourcefile-map.json

package.json

+9-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@augmint/js",
3-
"version": "0.3.2",
3+
"version": "0.3.3",
44
"description": "Augmint Javascript Library",
55
"keywords": [
66
"augmint javascript library A-EUR stablecoin web3 dapp"
@@ -41,14 +41,16 @@
4141
"cross-env": "5.2",
4242
"eslint": "5.16.0",
4343
"mocha": "6.1.4",
44-
"rollup": "1.12.3",
44+
"rollup": "1.12.4",
4545
"rollup-plugin-node-builtins": "2.1.2",
4646
"rollup-plugin-node-globals": "1.4.0",
4747
"rollup-plugin-typescript2": "0.21.1",
4848
"sinon": "7.3.2",
4949
"tslint": "5.16.0",
5050
"tslint-config-prettier": "1.18.0",
5151
"typechain": "0.3.14",
52+
"typedoc": "0.14.2",
53+
"typedoc-plugin-sourcefile-url": "1.0.4",
5254
"typescript": "3.4.3",
5355
"wait-on": "3.2.0"
5456
},
@@ -59,10 +61,13 @@
5961
"test": "yarn build && yarn cross-env NODE_ENV=test NODE_PATH=./ mocha 'test/**/*.test.js' --exit --timeout 5000",
6062
"ganache:start": "scripts/augmint-cli.sh ganache start --blockTime 1",
6163
"ganache:stop": "scripts/augmint-cli.sh ganache stop",
62-
"ganache:run": "scripts/augmint-cli.sh ganache run --blockTime 1"
64+
"ganache:run": "scripts/augmint-cli.sh ganache run --blockTime 1",
65+
"typedoc": "./generateDocs.sh"
6366
},
6467
"greenkeeper": {
65-
"ignore": ["@types/node"]
68+
"ignore": [
69+
"@types/node"
70+
]
6671
},
6772
"main": "dist/index.js",
6873
"module": "dist/index.es.js",

src/Exchange.ts

+63-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@ import { Rates } from "./Rates";
1010
import { Transaction } from "./Transaction";
1111
import { Ratio, Tokens, Wei } from "./units";
1212

13+
interface IMarketMatch {
14+
filledTokens: Tokens;
15+
filledEthers: Wei;
16+
limitPrice?: Ratio;
17+
averagePrice?: Ratio;
18+
}
19+
1320
export class OrderBook {
1421

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

32+
private static estimateMarketOrder<T extends IOrder>(orders: T[],
33+
tokens: Tokens,
34+
rate: Tokens,
35+
toTokens: (order: T) => Tokens): IMarketMatch {
36+
const ret: IMarketMatch = {
37+
filledTokens: Tokens.of(0),
38+
filledEthers: Wei.of(0)
39+
};
40+
41+
for (const order of orders) {
42+
const remaining = tokens.sub(ret.filledTokens);
43+
if (remaining.isZero()) {
44+
break;
45+
}
46+
47+
const fillTokens: Tokens = Tokens.min(toTokens(order), remaining);
48+
const fillEthers: Wei = fillTokens.toWeiAt(rate, order.price);
49+
50+
ret.filledTokens = ret.filledTokens.add(fillTokens);
51+
ret.filledEthers = ret.filledEthers.add(fillEthers);
52+
ret.limitPrice = order.price;
53+
}
54+
if (ret.limitPrice) {
55+
ret.averagePrice = rate.divToRatio(ret.filledTokens.toRate(ret.filledEthers));
56+
}
57+
58+
return ret;
59+
}
60+
2561
constructor(public buyOrders: IBuyOrder[], public sellOrders: ISellOrder[]) {
2662
buyOrders.sort(OrderBook.compareBuyOrders);
2763
sellOrders.sort(OrderBook.compareSellOrders);
@@ -47,13 +83,16 @@ export class OrderBook {
4783
if (!this.hasMatchingOrders()) {
4884
return { buyIds, sellIds, gasEstimate: 0 };
4985
}
86+
5087
const lowestSellPrice: Ratio = this.sellOrders[0].price;
5188
const highestBuyPrice: Ratio = this.buyOrders[0].price;
5289

5390
const clone = o => Object.assign({}, o);
54-
const buys: IBuyOrder[] = this.buyOrders.filter(o => o.price.gte(lowestSellPrice)).map(clone);
91+
const buys: IBuyOrder[] = this.buyOrders
92+
.filter(o => o.price.gte(lowestSellPrice)).map(clone);
5593

56-
const sells: ISellOrder[] = this.sellOrders.filter(o => o.price.lte(highestBuyPrice)).map(clone);
94+
const sells: ISellOrder[] = this.sellOrders
95+
.filter(o => o.price.lte(highestBuyPrice)).map(clone);
5796

5897
let buyIdx: number = 0;
5998
let sellIdx: number = 0;
@@ -98,6 +137,28 @@ export class OrderBook {
98137

99138
return { buyIds, sellIds, gasEstimate };
100139
}
140+
141+
/**
142+
* calculate price for n amount of token to buy
143+
* @return {object} simple buy data { tokens, ethers, limitPrice, averagePrice }
144+
* @param tokenAmount - amount of token to buy
145+
* @param ethFiatRate - current rate
146+
*/
147+
public estimateMarketBuy(tokenAmount: Tokens, ethFiatRate: Tokens): IMarketMatch {
148+
return OrderBook.estimateMarketOrder(this.sellOrders, tokenAmount, ethFiatRate,
149+
order => order.amount);
150+
}
151+
152+
/**
153+
* calculate price for n amount of token to sell
154+
* @return {object} simple buy data { tokens, ethers, limitPrice, averagePrice }
155+
* @param tokenAmount - amount of token to sell
156+
* @param ethFiatRate - current rate
157+
*/
158+
public estimateMarketSell(tokenAmount: Tokens, ethFiatRate: Tokens): IMarketMatch {
159+
return OrderBook.estimateMarketOrder(this.buyOrders, tokenAmount, ethFiatRate,
160+
order => order.amount.toTokensAt(ethFiatRate, order.price));
161+
}
101162
}
102163

103164
export interface IMatchingOrders {

src/units.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@ abstract class FixedPoint {
4040
return this.create(ceilDiv(this.amount.mul(Ratio.DIV_BN), ratio.amount));
4141
}
4242

43+
public divToRatio(other: this): Ratio {
44+
this.check(other);
45+
return new Ratio(this.amount.mul(Ratio.DIV_BN).divRound(other.amount));
46+
}
47+
4348
//
4449
// comparison
4550
//
@@ -125,6 +130,9 @@ export class Wei extends FixedPoint {
125130
return new Tokens(this.amount.mul(rate.amount).divRound(price.amount.mul(E12)));
126131
}
127132

133+
public toNumber(): number {
134+
return this.amount.toNumber() / Wei.DIV_BN.toNumber()
135+
}
128136
}
129137

130138
export class Tokens extends FixedPoint {
@@ -158,7 +166,6 @@ export class Tokens extends FixedPoint {
158166
this.check(ethers, Wei);
159167
return new Tokens(this.amount.mul(Wei.DIV_BN).divRound(ethers.amount));
160168
}
161-
162169
}
163170

164171

@@ -178,6 +185,5 @@ export class Ratio extends FixedPoint {
178185
public toNumber(): number {
179186
return this.amount.toNumber() / Ratio.DIV;
180187
}
181-
182188
}
183189

test/Exchange.simpleBuy.test.js

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
const { assert } = require("chai");
2+
3+
const { normalizeBN } = require("./testHelpers/normalize.js");
4+
const { Augmint, utils, Wei, Tokens, Ratio } = require("../dist/index.js");
5+
const loadEnv = require("./testHelpers/loadEnv.js");
6+
const OrderBook = Augmint.Exchange.OrderBook;
7+
8+
const config = loadEnv();
9+
10+
if (config.LOG) {
11+
utils.logger.level = config.LOG;
12+
}
13+
14+
const RATE = Tokens.of(400.00);
15+
16+
describe("calculate market orders", () => {
17+
it("should return matching info", () => {
18+
const buyOrders = [
19+
{ amount: Wei.of(0.0070), price: Ratio.of(1.2) },
20+
{ amount: Wei.of(0.0094), price: Ratio.of(1) },
21+
{ amount: Wei.of(0.0064), price: Ratio.of(0.7) }
22+
];
23+
24+
const sellOrders = [
25+
{ amount: Tokens.of(1), price: Ratio.of(1.02) },
26+
{ amount: Tokens.of(10), price: Ratio.of(1.03) }
27+
];
28+
29+
const orderBook = new OrderBook(buyOrders, sellOrders);
30+
31+
const sellResult = {
32+
filledTokens: Tokens.of(6),
33+
filledEthers: Wei.of(0.016165),
34+
limitPrice: Ratio.of(1),
35+
averagePrice: Ratio.of(1.077673)
36+
};
37+
38+
const sellMatches = orderBook.estimateMarketSell(Tokens.of(6), RATE);
39+
40+
assert.deepEqual(normalizeBN(sellResult), normalizeBN(sellMatches));
41+
42+
const buyResult = {
43+
filledTokens: Tokens.of(2),
44+
filledEthers: Wei.of(0.005125),
45+
limitPrice: Ratio.of(1.03),
46+
averagePrice: Ratio.of(1.02501)
47+
};
48+
49+
const buyMatches = orderBook.estimateMarketBuy(Tokens.of(2), RATE);
50+
51+
assert.deepEqual(normalizeBN(buyMatches), normalizeBN(buyResult));
52+
});
53+
54+
it("should work on empty order book", () => {
55+
const orderBook = new OrderBook([], []);
56+
const exp = {
57+
filledTokens: Tokens.of(0),
58+
filledEthers: Wei.of(0)
59+
};
60+
const buyMatches = orderBook.estimateMarketBuy(Tokens.of(100), RATE);
61+
const sellMatches = orderBook.estimateMarketSell(Tokens.of(100), RATE)
62+
assert.deepEqual(normalizeBN(buyMatches), normalizeBN(exp));
63+
assert.deepEqual(normalizeBN(sellMatches), normalizeBN(exp));
64+
});
65+
});

test/LoanManager.test.js

+1-19
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,7 @@ const chai = require("chai");
22
const { assert, expect } = chai;
33
chai.use(require("chai-exclude"));
44

5-
const BN = require("bn.js");
6-
BN.prototype.inspect = function() {
7-
return this.toString();
8-
};
9-
5+
const { normalizeBN } = require("./testHelpers/normalize.js");
106
const { takeSnapshot, revertSnapshot } = require("./testHelpers/ganache.js");
117
const { Augmint, utils, Wei, Tokens, Ratio } = require("../dist/index.js");
128
const { AugmintJsError } = Augmint.Errors;
@@ -17,20 +13,6 @@ if (config.LOG) {
1713
utils.logger.level = config.LOG;
1814
}
1915

20-
// deeply normalize all BN properties so they can be compared with deepEquals
21-
// NOTE: object graph must not have cycles
22-
function normalizeBN(obj) {
23-
Object.keys(obj).map((key, index) => {
24-
const o = obj[key];
25-
if (o instanceof BN) {
26-
obj[key] = new BN(o.toString());
27-
} else if (o instanceof Object) {
28-
obj[key] = normalizeBN(o);
29-
}
30-
});
31-
return obj;
32-
}
33-
3416
function mockProd(
3517
id,
3618
minDisbursedAmount,

test/testHelpers/normalize.js

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
const BN = require("bn.js");
2+
3+
BN.prototype.inspect = function() {
4+
return this.toString();
5+
};
6+
7+
module.exports = { normalizeBN };
8+
9+
// deeply normalize all BN properties so they can be compared with deepEquals
10+
// NOTE: object graph must not have cycles
11+
function normalizeBN(obj) {
12+
Object.keys(obj).map((key, index) => {
13+
const o = obj[key];
14+
if (o instanceof BN) {
15+
obj[key] = new BN(o.toString());
16+
} else if (o instanceof Object) {
17+
obj[key] = normalizeBN(o);
18+
}
19+
});
20+
return obj;
21+
}

0 commit comments

Comments
 (0)