replace alchemy with moralis

master
lza_menace 4 weeks ago
parent 0ecae09714
commit a73f98cd24

Binary file not shown.

@ -1,12 +1,12 @@
{ {
"dependencies": { "dependencies": {
"alchemy-sdk": "^3.5.0",
"better-sqlite3": "^11.7.0", "better-sqlite3": "^11.7.0",
"ethers": "^6.13.4", "ethers": "^6.13.4",
"express": "^4.21.2", "express": "^4.21.2",
"moralis": "^2.27.2",
"sqlite3": "^5.1.7" "sqlite3": "^5.1.7"
}, },
"name": "alchemy-nft-scraper", "name": "nft-sales-scraper",
"module": "index.ts", "module": "index.ts",
"devDependencies": { "devDependencies": {
"@types/bun": "latest" "@types/bun": "latest"

@ -1,18 +1,15 @@
const ALL_CONTRACTS = require('./contracts'); const ALL_CONTRACTS = require('./contracts');
const { Alchemy, Network } = require("alchemy-sdk"); import Moralis from 'moralis';
const { Database } = require('sqlite3'); const { Database } = require('sqlite3');
const fs = require('fs'); const fs = require('fs');
const db = new Database('./state/sqlite.db'); const db = new Database('./state/sqlite.db');
const config = { const config = {
apiKey: process.env.ALCHEMY_KEY, apiKey: process.env.MORALIS_KEY
network: Network.ETH_MAINNET,
}; };
const alchemy = new Alchemy(config);
async function sleep(sec) { async function sleep(sec) {
return new Promise((resolve) => setTimeout(resolve, Number(sec) * 1000)); return new Promise((resolve) => setTimeout(resolve, Number(sec) * 1000));
@ -34,7 +31,7 @@ class Scrape {
this.lastFile = `./state/${this.contractName}.txt`; this.lastFile = `./state/${this.contractName}.txt`;
} }
getpageKey() { getCursor() {
if (fs.existsSync(this.lastFile)) { if (fs.existsSync(this.lastFile)) {
return fs.readFileSync(this.lastFile).toString(); return fs.readFileSync(this.lastFile).toString();
} else { } else {
@ -44,26 +41,28 @@ class Scrape {
} }
async scrape() { async scrape() {
const pageKey = this.getpageKey() const cursor = this.getCursor()
if (pageKey === '') { if (cursor === '') {
console.log('no page key') console.log(`no cursor for ${this.contractName}. skipping`)
return return
} }
console.log(`[+] Scraping ${this.contractName} with pageKey ${pageKey}`) console.log(`[+] Scraping ${this.contractName}`);
const response = await alchemy.nft.getNftSales({ const response = await Moralis.EvmApi.nft.getNFTTrades({
chain: '0x1',
marketplace: 'opensea',
fromBlock: this.startBlock, fromBlock: this.startBlock,
contractAddress: this.contractAddress, address: this.contractAddress,
limit: process.env.LIMIT, limit: process.env.LIMIT,
order: 'asc', cursor: cursor
pageKey: pageKey
}); });
fs.writeFileSync(this.lastFile, response.pageKey || '') fs.writeFileSync(this.lastFile, response.json.cursor || '')
response.nftSales.map(async (sale) => { response.json.result.map(async (sale) => {
sale.token_ids.map(async (tokenId) => {
const rowExists = await new Promise((resolve) => { const rowExists = await new Promise((resolve) => {
db.get('SELECT * FROM events WHERE tx_hash = ? AND log_index = ?', [sale.transactionHash, sale.logIndex], (err, row) => { db.get('SELECT * FROM events WHERE tx_hash = ? AND token_id = ?', [sale.transaction_hash, tokenId], (err, row) => {
if (err) { resolve(false); } if (err) { resolve(false); }
resolve(row !== undefined); resolve(row !== undefined);
}); });
@ -73,23 +72,18 @@ class Scrape {
db.run(` db.run(`
INSERT INTO events VALUES ( INSERT INTO events VALUES (
"${this.contractAddress}", "${this.contractAddress}",
"${sale.buyerAddress}", "${sale.buyer_address}",
"${sale.sellerAddress}", "${sale.seller_address}",
"${sale.taker}", "${tokenId}",
"${sale.tokenId}", "${sale.price}",
"${sale.sellerFee.amount}",
"${sale.protocolFee.amount}",
"${sale.royaltyFee.amount}",
"", "",
"${sale.transactionHash}", "${sale.transaction_hash}",
"${sale.blockNumber}", "${sale.block_number}",
"${sale.logIndex}", "opensea",
"${sale.bundleIndex}", "${cursor}",
"${sale.marketplace}",
"${pageKey}",
0, 0 0, 0
)`); )`);
console.log(` ::: Inserted sale of ${this.contractName} #${sale.tokenId} in block ${sale.blockNumber} for ${sale.sellerFee.amount} wei.`) console.log(` ::: Inserted sale of ${this.contractName} #${tokenId} in block ${sale.block_number} for ${sale.price} wei.`)
} catch(err) { } catch(err) {
console.log(`Error when writing to database: ${err}`); console.log(`Error when writing to database: ${err}`);
return false; return false;
@ -97,6 +91,8 @@ class Scrape {
} }
}); });
});
await sleep(1); await sleep(1);
} }
@ -119,25 +115,21 @@ class Scrape {
contract text, contract text,
buyer text, buyer text,
seller text, seller text,
taker text,
token_id number, token_id number,
sale_price text, sale_price text,
protocol_fee text,
royalty_fee text,
tx_date text, tx_date text,
tx_hash text, tx_hash text,
block_number number, block_number number,
log_index number,
bundle_index number,
marketplace text, marketplace text,
page_key text, cursor text,
discord_sent number, discord_sent number,
twitter_sent number, twitter_sent number,
UNIQUE(tx_hash, log_index, bundle_index) UNIQUE(tx_hash, token_id)
);`, );`,
); );
}); });
} }
await Moralis.start(config);
while(true) { while(true) {
for(const contract in ALL_CONTRACTS) { for(const contract in ALL_CONTRACTS) {
if (process.env.ONLY && process.env.ONLY != contract) continue if (process.env.ONLY && process.env.ONLY != contract) continue

@ -61,7 +61,7 @@ app.get('/api/latest', (req, res) => {
app.get('/api/:contractAddress/data', (req, res) => { app.get('/api/:contractAddress/data', (req, res) => {
const results = []; const results = [];
const stmt = db.prepare(`select const stmt = db.prepare(`select
block_number block, (block_number / 100) * 100 block,
sum(sale_price/1000000000000000000.0) volume, sum(sale_price/1000000000000000000.0) volume,
avg(sale_price/1000000000000000000.0) average_price, avg(sale_price/1000000000000000000.0) average_price,
(select avg(sale_price/1000000000000000000.0) from (select * from events (select avg(sale_price/1000000000000000000.0) from (select * from events

Loading…
Cancel
Save