diff --git a/bun.lockb b/bun.lockb index 337d096..177ff5b 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 2aaa4fe..dbc28f6 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,13 @@ { "dependencies": { "alchemy-sdk": "^3.5.0", + "better-sqlite3": "^11.7.0", "ethers": "^6.13.4", + "express": "^4.21.2", "sqlite3": "^5.1.7" }, "name": "alchemy-nft-scraper", "module": "index.ts", - "type": "module", "devDependencies": { "@types/bun": "latest" }, diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..bfd7349 --- /dev/null +++ b/public/index.html @@ -0,0 +1,186 @@ + + + Art101 Sales Statistics + + + + + + + +

Art101 Sales Statistics

+

Source Code

+
+ Loading, please wait... +
+
+ + + diff --git a/src/server.js b/src/server.js new file mode 100644 index 0000000..9a33171 --- /dev/null +++ b/src/server.js @@ -0,0 +1,104 @@ +const ALL_CONTRACTS = require('./contracts'); + +const Database = require('better-sqlite3'); +const express = require('express'); + + +const app = express(); +const port = process.env.PORT || 3000; +const db = new Database('./state/sqlite.db', {readonly: true}); + + +app.use(express.json()); + +app.use('/', express.static('public')); + +app.use('/app', express.static('public')); + +app.get('/api/contracts', (req, res) => { + res.status(200).json(ALL_CONTRACTS) +}) + +app.get('/api/:contractAddress/events', (req, res) => { + const results = []; + const stmt = db.prepare(`select * + from events + where contract = '${req.params.contractAddress}' + collate nocase + order by block_number desc + limit 100 + `); + for (const entry of stmt.iterate()) { + results.push(entry); + } + res.status(200).json(results); +}); + +app.get('/api/token/:contractAddress/:tokenId/history', (req, res) => { + const results = []; + const stmt = db.prepare(`select * + from events + where token_id = ${req.params.tokenId} + and contract = '${req.params.contractAddress}' + collate nocase + order by block_number desc + `); + for (const entry of stmt.iterate()) { + results.push(entry); + } + res.status(200).json(results); +}); + +app.get('/api/latest', (req, res) => { + const stmt = db.prepare(`select * + from events + order by block_number desc + limit 1 + `); + res.status(200).json(stmt.get()); +}); + +app.get('/api/:contractAddress/data', (req, res) => { + const results = []; + const stmt = db.prepare(`select + block_number block, + sum(sale_price/1000000000000000000.0) volume, + avg(sale_price/1000000000000000000.0) average_price, + (select avg(sale_price/1000000000000000000.0) from (select * from events + where contract = '${req.params.contractAddress}' + collate nocase + order by sale_price + limit 10)) floor_price, + count(*) sales + from events ev + where contract = '${req.params.contractAddress}' + collate nocase + group by block + order by block + `); + for (const entry of stmt.iterate()) { + results.push(entry); + } + res.status(200).json(results); +}); + +app.get('/api/:contractAddress/platforms', (req, res) => { + const results = []; + const stmt = db.prepare(`select marketplace, + sum(sale_price/1000000000000000000.0) volume, + count(*) sales + from events + where contract = '${req.params.contractAddress}' + collate nocase + group by marketplace + order by sum(sale_price/1000000000000000000.0) desc + `); + for (const entry of stmt.iterate()) { + results.push(entry); + } + res.status(200).json(results); +}); + +app.listen(port, () => { + console.log(`Example app listening at http://localhost:${port}`); +}); \ No newline at end of file