{"id":18369832,"url":"https://github.com/cawfree/opensea-submarine","last_synced_at":"2025-11-09T00:02:38.208Z","repository":{"id":48306100,"uuid":"516797290","full_name":"cawfree/opensea-submarine","owner":"cawfree","description":"Ping. Ping. Ping.","archived":false,"fork":false,"pushed_at":"2023-03-13T20:29:23.000Z","size":105,"stargazers_count":32,"open_issues_count":1,"forks_count":2,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-03-22T04:31:47.244Z","etag":null,"topics":["erc1155","erc721","ethereum","exploit","nft","opensea","seaport"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cawfree.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-07-22T15:19:25.000Z","updated_at":"2024-12-26T10:40:12.000Z","dependencies_parsed_at":"2024-11-05T23:45:05.274Z","dependency_job_id":null,"html_url":"https://github.com/cawfree/opensea-submarine","commit_stats":{"total_commits":49,"total_committers":2,"mean_commits":24.5,"dds":"0.020408163265306145","last_synced_commit":"90783a591baa17f64101e70d6bc6b01c71b517b4"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cawfree%2Fopensea-submarine","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cawfree%2Fopensea-submarine/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cawfree%2Fopensea-submarine/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cawfree%2Fopensea-submarine/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cawfree","download_url":"https://codeload.github.com/cawfree/opensea-submarine/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247128704,"owners_count":20888232,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["erc1155","erc721","ethereum","exploit","nft","opensea","seaport"],"created_at":"2024-11-05T23:32:28.739Z","updated_at":"2025-11-09T00:02:38.172Z","avatar_url":"https://github.com/cawfree.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003e ⚠️ This repository is __deprecated__.\n\u003e\n\u003e 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`](https://github.com/cawfree/sameorigin).\n\n# opensea-submarine\n\n[watch me breakdown](https://twitter.com/cawfree/status/1550505696747307013)\n\n[__OpenSea__](https://opensea.io) is the world's foremost NFT marketplace which takes great care to protect its API from abuse by [__ETH__](https://ethereum.org/en/)-hungry robots who are desperate to discover profit opportunities the fastest.\n\nOpenSea makes this task difficult in a number of ways:\n  - Any request to the backend must be precisely-defined to satisfy strong [__CloudFlare__](https://www.cloudflare.com/en-gb/) protection.\n  - Robust client-side session management and adherence imposes additional complexity during request formation which dramatically complicates attempts to programmatically `fetch` the API.\n  - The backend enforces that the structure of an individual request must resolve to a known checksum.\n\nIf that wasn't enough, the successfully returned contents of pages rendered by OpenSea's [__SPA__](https://en.wikipedia.org/wiki/Single-page_application) are highly obfuscated to make the task of [_manual scraping_](https://github.com/cawfree/opensea-floor-looks-rare) slow, unreliable and limited in scalability.\n\nBy using a [_stealthy_](https://www.npmjs.com/package/puppeteer-extra-plugin-stealth) flavour of [__Puppeteer__](https://developer.chrome.com/docs/puppeteer/), this repository demonstrates that a user can __hijack__ client-side [GraphQL](https://graphql.org/) requests and repurpose them for custom queries. This enables the client to squat on the complex trusted setup and abstract away request complexity.\n\n## 🚀 getting started\n\nUsing [__Yarn__](https://yarnpkg.com/):\n\n```bash\nyarn add opensea-submarine\n```\n\n## ✏️ usage\n\nThis package exports an [__Express__](https://github.com/expressjs/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:\n\n```typescript\nimport cors from 'cors';\nimport express from 'express';\nimport axios from 'axios';\n\nimport {proxyMiddleware} from 'opensea-submarine';\n\nconst openSeaEnvironment = {\n  graphQLUri: 'https://opensea.io/__api/graphql/',\n  eventHistoryUri: 'https://opensea.io/collection/boredapeyachtclub?tab=activity',\n  privacyUri: 'https://opensea.io/privacy',\n};\n\nconst proxyContext = await createProxyContext(openSeaEnvironment);\n\nconst server = await new Promise\u003cServer\u003e(\n  async resolve =\u003e {\n    const server = express()\n      .use(cors())\n      .use(await proxyMiddleware({\n        debug: true,\n        proxyContext,\n      }))\n      .listen(3000, () =\u003e resolve(server));\n  },\n);\n```\n\nThen you're free to query the middleware using queries that are recognized by OpenSea:\n\n```typescript\nimport axios from 'axios';\n\nconst {data} = await axios({\n  url: 'http://localhost:3000/graphql',\n  method: 'post',\n  data: {\"query\":\"query useIsEditableQuery(\\n  $collection: CollectionSlug!\\n) {\\n  collection(collection: $collection) {\\n    isEditable\\n    id\\n  }\\n}\\n\",\"variables\":{\"collection\": \"boredapeyachtclub\"}},\n});\n\n// {\"collection\":{\"isEditable\":false,\"id\":\"Q29sbGVjdGlvblR5cGU6NDg4NjIx\"}}\n```\n\nYou can check out the [__examples__](./scripts/start.ts) for additional insight.\n\nDevelopers 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.\n\nBy contrast, request `variables` are permitted to change freely.\n\n## ✌️ license\n[__MIT__](./LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcawfree%2Fopensea-submarine","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcawfree%2Fopensea-submarine","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcawfree%2Fopensea-submarine/lists"}