Skip to content

Commit

Permalink
Basic layout
Browse files Browse the repository at this point in the history
  • Loading branch information
protinam committed Feb 10, 2018
1 parent 143c3ae commit 9504f30
Show file tree
Hide file tree
Showing 9 changed files with 1,236 additions and 2 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
config.js
node_modules
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,16 @@

Open-source 0x orderbook server, written for ForkDelta.

Three-tier: [Varnish](https://varnish-cache.org/) in front, Node HTTP server, PostgreSQL backing datastore.
Three-tier: [Varnish](https://varnish-cache.org/) in front (not yet implemented), Node HTTP server, PostgreSQL backing datastore.

### Operation

Copy [config.example.js](config/config.example.js) to `config/config.js`, edit to suit your requirements, then simply run:

```bash
node index.js
```

### Development Information

#### Setup
Expand Down
1 change: 1 addition & 0 deletions config/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
config.js
4 changes: 4 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
const server = require('./src/server')
const config = require('./config/config')

server.go(config)
11 changes: 10 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,14 @@
"main": "index.js",
"repository": "https://github.com/protinam/0x-orderbook-server.git",
"author": "Protinam <[email protected]>",
"license": "MIT"
"license": "MIT",
"dependencies": {
"0x.js": "^0.32.2",
"@0xproject/json-schemas": "^0.7.9",
"body-parser": "^1.18.2",
"express": "^4.16.2",
"helmet": "^3.11.0",
"pg": "^7.4.1",
"sequelize": "^4.33.2"
}
}
75 changes: 75 additions & 0 deletions src/db.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
const Sequelize = require('sequelize')
const { ZeroEx } = require('0x.js')

module.exports = (config) => {
const sequelize = new Sequelize(config.database, config.username, config.password, config)

const Order = sequelize.define('order', {
hash: {type: Sequelize.TEXT, allowNull: false, primaryKey: true},
exchangeContractAddress: {type: Sequelize.TEXT, allowNull: false},
maker: {type: Sequelize.TEXT, allowNull: false},
taker: {type: Sequelize.TEXT, allowNull: false},
makerTokenAddress: {type: Sequelize.TEXT, allowNull: false},
takerTokenAddress: {type: Sequelize.TEXT, allowNull: false},
feeRecipient: {type: Sequelize.TEXT, allowNull: false},
makerTokenAmount: {type: Sequelize.TEXT, allowNull: false},
takerTokenAmount: {type: Sequelize.TEXT, allowNull: false},
makerFee: {type: Sequelize.TEXT, allowNull: false},
takerFee: {type: Sequelize.TEXT, allowNull: false},
expirationUnixTimestampSec: {type: Sequelize.TEXT, allowNull: false},
salt: {type: Sequelize.TEXT, allowNull: false},
v: {type: Sequelize.TEXT, allowNull: false},
r: {type: Sequelize.TEXT, allowNull: false},
s: {type: Sequelize.TEXT, allowNull: false}
})

const orderFromJSON = (order) => {
const hash = ZeroEx.getOrderHashHex(order)
return {
hash: hash,
exchangeContractAddress: order.exchangeContractAddress,
maker: order.maker,
taker: order.taker,
makerTokenAddress: order.makerTokenAddress,
takerTokenAddress: order.takerTokenAddress,
feeRecipient: order.feeRecipient,
makerTokenAmount: order.makerTokenAmount,
takerTokenAmount: order.takerTokenAmount,
makerFee: order.makerFee,
takerFee: order.takerFee,
expirationUnixTimestampSec: order.expirationUnixTimestampSec,
salt: order.salt,
v: order.ecSignature.v,
r: order.ecSignature.r,
s: order.ecSignature.s
}
}

const orderToJSON = (order) => ({
exchangeContractAddress: order.exchangeContractAddress,
maker: order.maker,
taker: order.taker,
makerTokenAddress: order.makerTokenAddress,
takerTokenAddress: order.takerTokenAddress,
feeRecipient: order.feeRecipient,
makerTokenAmount: order.makerTokenAmount,
takerTokenAmount: order.takerTokenAmount,
makerFee: order.makerFee,
takerFee: order.takerFee,
expirationUnixTimestampSec: order.expirationUnixTimestampSec,
salt: order.salt,
ecSignature: {
v: order.v,
r: order.r,
s: order.s
}
})

return {
sequelize,
Op: Sequelize.Op,
Order,
orderFromJSON,
orderToJSON
}
}
16 changes: 16 additions & 0 deletions src/scan.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const scan = ({ sequelize, Order }, config) => {
const func = () => {
const start = Date.now() / 1000
Order.findAll().then(orders => {
const end = Date.now() / 1000
const diff = end - start
config.log('debug', 'Scan complete in ' + Math.round(diff * 1000) / 1000 + 's')
})
}

setInterval(func, config.scanInterval)
}

module.exports = {
scan: scan
}
149 changes: 149 additions & 0 deletions src/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
const express = require('express')
const bodyParser = require('body-parser')
const helmet = require('helmet')

const { schemas, SchemaValidator } = require('@0xproject/json-schemas')

const db = require('./db')
const { scan } = require('./scan')

const withPayload = (schema, func) => {
return (req, res) => {
const payload = req.body
const schemaValidator = new SchemaValidator()
const valid = schemaValidator.validate(payload, schema)
console.log(valid)
if (valid) {
func(req, res)
} else {
res.status(100)
res.json({
code: 100,
reason: 'Schema validation failed',
validationErrors: []
})
}
}
}

const paged = (func) => {
return (req, res) => {
const page = req.query.page !== undefined ? parseInt(req.query.page) : 1
const perPage = Math.min(req.query.per_page !== undefined ? parseInt(req.query.per_page) : 20, 100)
const limit = perPage
const offset = perPage * (page - 1)
func(limit, offset, req, res)
}
}

const go = (config) => {
const { sequelize, Op, Order, orderToJSON, orderFromJSON } = db(config.sequelize)

const app = express()
app.use(bodyParser.json())
if (config.production) {
app.use(helmet())
}

const router = express.Router()

router.get('/token_pairs', (req, res) => {
res.status(501)
res.json({
code: 501,
reason: 'Endpoint not supported, all token pairs will be accepted'
})
})

router.get('/orders', paged((limit, offset, req, res) => {
var query = {limit, offset, where: {}}
if (req.query.makerTokenAddress) {
query.where.makerTokenAddress = req.query.makerTokenAddress
}
if (req.query.takerTokenAddress) {
query.where.takerTokenAddress = req.query.takerTokenAddress
}
if (req.query.makerTokenAddress && req.query.takerTokenAddress) {
query.order = sequelize.literal('(takerTokenAmount / makerTokenAmount) ASC (makerFee + takerFee) ASC expirationUnixTimestampSec ASC')
}
if (req.query.tokenAddress) {
query.where[Op.or] = [{makerTokenAddress: req.query.tokenAddress, takerTokenAddress: req.query.tokenAddress}]
}
if (req.query.maker) {
query.where.maker = req.query.maker
}
if (req.query.taker) {
query.where.taker = req.query.taker
}
if (req.query.trader) {
query.where[Op.or] = [{maker: req.query.trader, taker: req.query.trader}]
}
if (req.query.feeRecipient) {
query.where.feeRecipient = req.query.feeRecipient
}
Order.findAll(query).then(orders => {
res.json(orders.map(orderToJSON))
})
}))

router.get('/order/:hash', (req, res) => {
Order.findOne({where: {hash: req.params.hash}}).then(order => {
res.json(orderToJSON(order))
}).catch(() => {
res.status(404)
})
})

router.get('/orderbook', paged((limit, offset, req, res) => {
const baseTokenAddress = req.query.baseTokenAddress
const quoteTokenAddress = req.query.quoteTokenAddress
const bidQuery = {
where: {makerTokenAddress: baseTokenAddress, takerTokenAddress: quoteTokenAddress},
order: sequelize.literal('(takerTokenAmount / makerTokenAmount) DESC (makerFee + takerFee) ASC expirationUnixTimestampSec ASC'),
limit,
offset
}
const askQuery = {
where: {makerTokenAddress: quoteTokenAddress, takerTokenAddress: baseTokenAddress},
order: sequelize.literal('(takerTokenAmount / makerTokenAmount) ASC (makerFee + takerFee) ASC expirationUnixTimestampSec ASC'),
limit,
offset
}
Order.findAll(bidQuery).then(bids => {
Order.findAll(askQuery).then(asks => {
bids = bids.map(orderToJSON)
asks = asks.map(orderToJSON)
res.json({bids, asks})
})
})
}))

router.post('/order', withPayload(schemas.signedOrderSchema, (req, res) => {
Order.create(orderFromJSON(req.body)).then(() => {
res.status(201)
})
}))

router.post('/fees', withPayload(schemas.relayerApiFeesPayloadSchema, (req, res) => {
res.json({
feeRecipient: config.feeRecipient,
makerFee: config.calculateMakerFee(req.body),
takerFee: config.calculateTakerFee(req.body)
})
}))

app.use('v0', router)

sequelize
.sync()
.then(() => {
scan({ sequelize, Order }, config)
app.listen(config.port, () => {
config.log('info', 'Server listening on port ' + config.port)
})
})
}

module.exports = {
go: go
}
Loading

0 comments on commit 9504f30

Please sign in to comment.