cawfree / opensea-submarine

Ping. Ping. Ping.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

⚠️ This repository is deprecated.

The technique of Same-Origin-Resource-Crossing, which this repository demonstrated can be used to attain access to closed-off APIs, has been generalized in @cawfree/sameorigin.

opensea-submarine

watch me breakdown

OpenSea is the world's foremost NFT marketplace which takes great care to protect its API from abuse by ETH-hungry robots who are desperate to discover profit opportunities the fastest.

OpenSea makes this task difficult in a number of ways:

  • Any request to the backend must be precisely-defined to satisfy strong CloudFlare protection.
  • Robust client-side session management and adherence imposes additional complexity during request formation which dramatically complicates attempts to programmatically fetch the API.
  • The backend enforces that the structure of an individual request must resolve to a known checksum.

If that wasn't enough, the successfully returned contents of pages rendered by OpenSea's SPA are highly obfuscated to make the task of manual scraping slow, unreliable and limited in scalability.

By using a stealthy flavour of Puppeteer, this repository demonstrates that a user can hijack client-side GraphQL requests and repurpose them for custom queries. This enables the client to squat on the complex trusted setup and abstract away request complexity.

🚀 getting started

Using Yarn:

yarn add opensea-submarine

✏️ usage

This package exports an Express middleware which emulates a conventional GraphQL interface. GraphQL requests captured by the middleware are validated, sanitized and curried over into OpenSea's backend via request-squatting:

import cors from 'cors';
import express from 'express';
import axios from 'axios';

import {proxyMiddleware} from 'opensea-submarine';

const openSeaEnvironment = {
  graphQLUri: 'https://opensea.io/__api/graphql/',
  eventHistoryUri: 'https://opensea.io/collection/boredapeyachtclub?tab=activity',
  privacyUri: 'https://opensea.io/privacy',
};

const proxyContext = await createProxyContext(openSeaEnvironment);

const server = await new Promise<Server>(
  async resolve => {
    const server = express()
      .use(cors())
      .use(await proxyMiddleware({
        debug: true,
        proxyContext,
      }))
      .listen(3000, () => resolve(server));
  },
);

Then you're free to query the middleware using queries that are recognized by OpenSea:

import axios from 'axios';

const {data} = await axios({
  url: 'http://localhost:3000/graphql',
  method: 'post',
  data: {"query":"query useIsEditableQuery(\n  $collection: CollectionSlug!\n) {\n  collection(collection: $collection) {\n    isEditable\n    id\n  }\n}\n","variables":{"collection": "boredapeyachtclub"}},
});

// {"collection":{"isEditable":false,"id":"Q29sbGVjdGlvblR5cGU6NDg4NjIx"}}

You can check out the examples for additional insight.

Developers are reminded that the query content and x-signed-query header must be identical to real graphql/ requests sourced from OpenSea, which can be found in your browser's Networking tab.

By contrast, request variables are permitted to change freely.

✌️ license

MIT

About

Ping. Ping. Ping.

License:MIT License


Languages

Language:TypeScript 100.0%