Skip to content

Commit

Permalink
fix: use integer satoshi quantities
Browse files Browse the repository at this point in the history
This commit changes all `quantity` values to be represented by integer
satoshi amounts (10 ^ -8 of a full unit) rather than fractional/floating
full units. This applies both to the internal representations as well as
the gRPC API definition. This is done to prevent any rounding errors
when adding or subtracting fractional units.

Output and arguments for the cli remain unchanged and still deal with
full units.

Fixes #740.

BREAKING CHANGE: Database and p2p field type changes
  • Loading branch information
sangaman committed Apr 9, 2019
1 parent a5a06e1 commit 3d12ada
Show file tree
Hide file tree
Showing 23 changed files with 158 additions and 148 deletions.
22 changes: 11 additions & 11 deletions docs/api.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion lib/cli/commands/executeswap.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { callback, loadXudClient } from '../command';
import { Arguments } from 'yargs';
import { ExecuteSwapRequest } from '../../proto/xudrpc_pb';
import { SATOSHIS_PER_COIN } from '../utils';

export const command = 'executeswap <pair_id> <order_id> [quantity]';

Expand All @@ -23,6 +24,6 @@ export const handler = (argv: Arguments) => {
const request = new ExecuteSwapRequest();
request.setOrderId(argv.order_id);
request.setPairId(argv.pair_id);
request.setQuantity(argv.quantity);
request.setQuantity(argv.quantity * SATOSHIS_PER_COIN);
loadXudClient(argv).executeSwap(request, callback(argv));
};
3 changes: 2 additions & 1 deletion lib/cli/commands/listorders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { callback, loadXudClient } from '../command';
import { ListOrdersRequest, ListOrdersResponse, Order, OrderSide } from '../../proto/xudrpc_pb';
import Table, { HorizontalTable } from 'cli-table3';
import colors from 'colors/safe';
import { SATOSHIS_PER_COIN } from '../utils';

type FormattedTradingPairOrders = {
pairId: string,
Expand All @@ -29,7 +30,7 @@ const addSide = (orderSide: Order.AsObject[]): string[] => {
if (order) {
const isOwn = order.isOwnOrder ? 'X' : '';
return [
order.quantity.toFixed(8),
(order.quantity / SATOSHIS_PER_COIN).toFixed(8),
order.price.toString(),
isOwn,
].map(i => order.isOwnOrder ? colors.cyan(i) : i);
Expand Down
3 changes: 2 additions & 1 deletion lib/cli/commands/removeorder.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { callback, loadXudClient } from '../command';
import { Arguments } from 'yargs';
import { RemoveOrderRequest } from '../../proto/xudrpc_pb';
import { SATOSHIS_PER_COIN } from '../utils';

export const command = 'removeorder <order_id> [quantity]';

Expand All @@ -20,7 +21,7 @@ export const handler = (argv: Arguments) => {
const request = new RemoveOrderRequest();
request.setOrderId(argv.order_id);
if (argv.quantity) {
request.setQuantity(argv.quantity);
request.setQuantity(argv.quantity * SATOSHIS_PER_COIN);
}
loadXudClient(argv).removeOrder(request, callback(argv));
};
12 changes: 7 additions & 5 deletions lib/cli/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { Arguments, Argv } from 'yargs';
import { callback, loadXudClient } from './command';
import { PlaceOrderRequest, PlaceOrderEvent, OrderSide, PlaceOrderResponse, Order, SwapSuccess, SwapFailure } from '../proto/xudrpc_pb';

export const SATOSHIS_PER_COIN = 10 ** 8;

export const orderBuilder = (argv: Argv, command: string) => argv
.option('quantity', {
type: 'number',
Expand Down Expand Up @@ -33,7 +35,7 @@ export const orderHandler = (argv: Arguments, isSell = false) => {
const numericPrice = Number(argv.price);
const priceStr = argv.price.toLowerCase();

request.setQuantity(argv.quantity);
request.setQuantity(argv.quantity * SATOSHIS_PER_COIN);
request.setSide(isSell ? OrderSide.SELL : OrderSide.BUY);
request.setPairId(argv.pair_id.toUpperCase());

Expand Down Expand Up @@ -95,22 +97,22 @@ const formatPlaceOrderOutput = (response: PlaceOrderResponse.AsObject) => {

const formatInternalMatch = (order: Order.AsObject) => {
const baseCurrency = getBaseCurrency(order.pairId);
console.log(`matched ${order.quantity} ${baseCurrency} @ ${order.price} with own order ${order.id}`);
console.log(`matched ${order.quantity / SATOSHIS_PER_COIN} ${baseCurrency} @ ${order.price} with own order ${order.id}`);
};

const formatSwapSuccess = (swapSuccess: SwapSuccess.AsObject) => {
const baseCurrency = getBaseCurrency(swapSuccess.pairId);
console.log(`swapped ${swapSuccess.quantity} ${baseCurrency} with peer order ${swapSuccess.orderId}`);
console.log(`swapped ${swapSuccess.quantity / SATOSHIS_PER_COIN} ${baseCurrency} with peer order ${swapSuccess.orderId}`);
};

const formatSwapFailure = (swapFailure: SwapFailure.AsObject) => {
const baseCurrency = getBaseCurrency(swapFailure.pairId);
console.log(`failed to swap ${swapFailure.quantity} ${baseCurrency} with peer order ${swapFailure.orderId}`);
console.log(`failed to swap ${swapFailure.quantity / SATOSHIS_PER_COIN} ${baseCurrency} with peer order ${swapFailure.orderId}`);
};

const formatRemainingOrder = (order: Order.AsObject) => {
const baseCurrency = getBaseCurrency(order.pairId);
console.log(`remaining ${order.quantity} ${baseCurrency} entered the order book as ${order.id}`);
console.log(`remaining ${order.quantity / SATOSHIS_PER_COIN} ${baseCurrency} entered the order book as ${order.id}`);
};

const getBaseCurrency = (pairId: string) => pairId.substring(0, pairId.indexOf('/'));
4 changes: 2 additions & 2 deletions lib/db/models/Order.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ export default (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes)
id: { type: DataTypes.STRING, primaryKey: true, allowNull: false },
nodeId: { type: DataTypes.INTEGER, allowNull: true },
localId: { type: DataTypes.STRING, allowNull: true },
initialQuantity: { type: DataTypes.DECIMAL(8), allowNull: false },
initialQuantity: { type: DataTypes.BIGINT, allowNull: false },
pairId: { type: DataTypes.STRING, allowNull: false },
price: {
type: DataTypes.DECIMAL(8),
type: DataTypes.DOUBLE,
allowNull: true,
set(this: db.OrderInstance, value: number) {
if (value === 0 || value === Number.MAX_VALUE) {
Expand Down
4 changes: 2 additions & 2 deletions lib/db/models/SwapDeal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ export default (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes)
nodeId: { type: DataTypes.INTEGER, allowNull: false },
orderId: { type: DataTypes.STRING, allowNull: false },
localId: { type: DataTypes.STRING, allowNull: false },
proposedQuantity: { type: DataTypes.DECIMAL(8), allowNull: false },
quantity: { type: DataTypes.DECIMAL(8), allowNull: true },
proposedQuantity: { type: DataTypes.BIGINT, allowNull: false },
quantity: { type: DataTypes.BIGINT, allowNull: true },
takerAmount: { type: DataTypes.BIGINT , allowNull: false },
takerCurrency: { type: DataTypes.STRING , allowNull: false },
takerPubKey: { type: DataTypes.STRING , allowNull: true },
Expand Down
2 changes: 1 addition & 1 deletion lib/db/models/Trade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export default (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes)
makerOrderId: { type: DataTypes.STRING, allowNull: false },
takerOrderId: { type: DataTypes.STRING, allowNull: true },
rHash: { type: DataTypes.STRING, allowNull: true },
quantity: { type: DataTypes.DECIMAL(8), allowNull: false },
quantity: { type: DataTypes.BIGINT, allowNull: false },
};

const options: Sequelize.DefineOptions<db.TradeInstance> = {
Expand Down
6 changes: 4 additions & 2 deletions lib/orderbook/OrderBook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -498,8 +498,10 @@ class OrderBook extends EventEmitter {
}

/**
* Removes an order from the order book by its local id. Throws an error if the specified pairId
* is not supported or if the order to cancel could not be found.
* Removes all or part of an order from the order book by its local id. Throws an error if the
* specified pairId is not supported or if the order to cancel could not be found.
* @param quantityToRemove the quantity to remove from the order, if undefined then the entire
* order is removed.
* @returns any quantity of the order that was on hold and could not be immediately removed.
*/
public removeOwnOrderByLocalId = (localId: string, quantityToRemove?: number) => {
Expand Down
4 changes: 2 additions & 2 deletions lib/orderbook/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export enum PlaceOrderEventType {

/** An order without a price that is intended to match against any available orders on the opposite side of the book for its trading pair. */
type MarketOrder = {
/** The number of currently available base currency tokens for the order. */
/** The number of currently satoshis (or equivalent) for the order. */
quantity: number;
/** A trading pair symbol with the base currency first followed by a '/' separator and the quote currency */
pairId: string;
Expand Down Expand Up @@ -70,7 +70,7 @@ type Remote = {
type Stamp = OrderIdentifier & {
/** Epoch timestamp when this order was created locally. */
createdAt: number;
/** The number of base currency tokens initially available for the order, before any actions such as trades reduced the available quantity. */
/** The number of satoshis (or equivalent) initially available for the order, before any actions such as trades reduced the available quantity. */
initialQuantity: number;
};

Expand Down
47 changes: 24 additions & 23 deletions lib/proto/xudrpc.swagger.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 3d12ada

Please sign in to comment.