Skip to content

Instantly share code, notes, and snippets.

@cryppadotta
Last active July 28, 2022 06:26
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cryppadotta/a3e5ad0a8a167089e6c7ae9d65130666 to your computer and use it in GitHub Desktop.
Save cryppadotta/a3e5ad0a8a167089e6c7ae9d65130666 to your computer and use it in GitHub Desktop.
OpenSea Floor Price Depth

OpenSea Floor Depth Calculator

Find out how much ETH is required to move to a certain floor price.

yarn
./node_modules/.bin/ts-node floor-depth.ts --slug forgottenruneswizardscult

NOTE: OpenSea limits to 50 listings per page (:eyeroll:) so this takes an eternity (hours) to run

import { BigNumber } from "@ethersproject/bignumber";
import * as chalk from "chalk";
import { formatEther } from "ethers/lib/utils";
import * as fs from "fs";
import { forEach, groupBy, last } from "lodash";
import * as fetch from "node-fetch";
import * as ora from "ora";
import * as yargs from "yargs";
const argv = yargs
.usage("$0")
.option("slug", {
describe: "open-sea slug",
string: true,
required: true,
default: "forgottenruneswizardscult"
})
.option("rpc", {
describe: "The URL to your provider",
string: true,
required: true,
default: "https://cloudflare-eth.com/"
})
.help("help").argv;
const runes = ["Φ", "Ψ", "λ", "ϐ", "ҩ", "Ӝ", "⊙", "⚧", "⚭", "Ω"];
async function fetchOrderPage({
slug,
spinner,
page = 0
}: {
slug: string;
spinner: any;
page: number;
}) {
spinner.text = `Fetching page ${chalk.blue("#" + page)}`;
const url = `https://api.opensea.io/wyvern/v1/orders?collection_slug=${slug}&bundled=false&include_bundled=false&include_invalid=false&side=1&limit=50&offset=${page}&order_by=created_date&order_direction=desc`;
const response = await (
await fetch(url, {
method: "GET",
headers: { Accept: "application/json" }
})
).json();
return response.orders;
}
const WETH = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2";
const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
function preprocessOrders({ orders }) {
let lowestOrders = [];
const ordersByToken = groupBy(orders, (o) => o.asset.token_id);
forEach(ordersByToken, (tokensOrders, tokenId) => {
const ethOrders = tokensOrders.filter(
(o) => o.payment_token === WETH || o.payment_token === ZERO_ADDRESS
);
const sortedByPrice = ethOrders.sort((a, b) =>
BigNumber.from(a.base_price).gt(BigNumber.from(b.base_price)) ? 1 : -1
);
lowestOrders.push(sortedByPrice[0]);
});
return lowestOrders.sort((a, b) =>
BigNumber.from(a.base_price).gt(BigNumber.from(b.base_price)) ? 1 : -1
);
}
async function run(argv: any) {
const spinner = ora({
text: "Downloading",
spinner: { interval: 80, frames: runes },
prefixText: "🧙‍♀️"
}).start();
let page = 0;
let lastCount = 0;
let orders = [];
let maxPages = 5000;
do {
const newOrders = await fetchOrderPage({ slug: argv.slug, spinner, page });
lastCount = newOrders.length;
orders = orders.concat(newOrders);
page += 1;
} while (lastCount > 0 && page <= maxPages);
/**
* the same user might have multiple open orders at different price points
* because it costs gas to cancel an order so users often add a new order at a
* lower price point. we're trying to calculate the lowest price that could
* move the floor, so we want to ignore all but the lowest open sell order
*/
const processedOrders = preprocessOrders({ orders });
fs.writeFileSync("depth.csv", "");
let sum = BigNumber.from(0);
processedOrders.map((o) => {
const row = [
o.asset.token_id,
o.base_price,
sum.toString(),
formatEther(sum)
];
console.log(...row);
fs.appendFileSync("depth.csv", row.join(",") + "\n");
sum = sum.add(BigNumber.from(o.base_price));
});
spinner.color = "green";
spinner.text = "Complete";
spinner.succeed();
}
run(argv);
{
"name": "floor-depth",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"chalk": "^4.1.0",
"ethers": "^5.0.26",
"lodash": "^4.17.20",
"ora": "^5.3.0",
"ts-node": "^9.0.0",
"typescript": "^4.0.5",
"yargs": "^16.1.0"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment