[
  {
    "path": ".gitignore",
    "content": ".DS_Store\n.env\nnode_modules/\nyarn*\ntmp*\n\n# Debugging tools\nscripts/collection.ts\nscripts/sharp.ts\n\nlangchain_examples/python/__pycache__\n*json"
  },
  {
    "path": "DISCLAIMER.md",
    "content": "# Disclaimer\n\nAll claims, content, designs, algorithms, estimates, roadmaps, specifications, and performance measurements described in this project are done with the good faith efforts Solana Labs, Inc. and its affiliates (\"SL\"). It is up to the reader to check and validate their accuracy and truthfulness. Furthermore nothing in this project constitutes a solicitation for investment.\nAny content produced by SL or developer resources that SL provides have not been subject to audit and are for educational and inspiration purposes only. SL does not encourage, induce or sanction the deployment, integration or use of any such applications (including the code comprising the Solana blockchain protocol) in violation of applicable laws or regulations and hereby prohibits any such deployment, integration or use. This includes use of any such applications by the reader (a) in violation of export control or sanctions laws of the United States or any other applicable jurisdiction, (b) if the reader is located in or ordinarily resident in a country or territory subject to comprehensive sanctions administered by the U.S. Office of Foreign Assets Control (OFAC), or (c) if the reader is or is working on behalf of a Specially Designated National (SDN) or a person subject to similar blocking or denied party prohibitions.\nThe reader should be aware that U.S. export control and sanctions laws prohibit U.S. persons (and other persons that are subject to such laws) from transacting with persons in certain countries and territories or that are on the SDN list. As a project based primarily on open-source software, it is possible that such sanctioned persons may nevertheless bypass prohibitions, obtain the code comprising the Solana blockchain protocol (or other project code or applications) and deploy, integrate, or otherwise use it. Accordingly, there is a risk to individuals that other persons using the Solana blockchain protocol may be sanctioned persons and that transactions with such persons would be a violation of U.S. export controls and sanctions law. This risk applies to individuals, organizations, and other ecosystem participants that deploy, integrate, or use the Solana blockchain protocol code directly (e.g., as a node operator), and individuals that transact on the Solana blockchain through light clients, third party interfaces, and/or wallet software.\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright 2023 Solana Labs, Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "# Solana ChatGPT Plugin\nA ChatGPT plugin for Solana. Install as an unverified plugin with url `https://chatgpt.solanalabs.com`.\n\n<div>\n<img width=\"350\" alt=\"Search NFTs in ChatGPT\" src=\"https://user-images.githubusercontent.com/7481857/231182274-40b42f0e-5e5d-4050-9e31-2f75375481c1.png\">\n</div>\n\n## Endpoints\n\nChatGPT can POST to the following resources, as described by `.well-known/openapi.yaml`.\n\n\n<details>\n<summary>\n/getAccountInfo { address }\n</summary>\n\nReturns the output of `getAccountInfo` method from the RPC with buffer data, and if it can be deserialized by its program IDL, then the response payload has additional field called `extended` that has a JSON serialized string of the anchor data. Chat GPT's plugin model seems to be able to read this pretty well.\n\n```json\n{\n  ...,\n  \"extended\": \"{\\\"authority\\\":\\\"8fbqVvpK3Dj7fdP2c8JJhtD7Zy3n9qtwAeGfbkgPu625\\\",\\\"numMinted\\\":50}\"\n}\n```\n</details>\n\n<details>\n<summary>/getBalance { address }</summary>\n\nReturns\n```json\n{\n  \"sol\": 0.40296\n}\n```\n</details>\n\n<details>\n<summary>/getAssetsByOwner { address }</summary>\n\nReturns the assets returned by the [Metaplex Read API spec](https://github.com/metaplex-foundation/api-specifications/blob/main/specifications/read_api/openrpc_spec.json)\n</details>\n\n<details>\n<summary>/getTransaction { signature } </summary>\n\nAccepts\n```json\n{\n  \"signature\": \"h51pjmFcn8LkxejofUQoDYkyubUKaB7bNtyMMSCCamSEYRutS2G2vm2w1ERShko8boRqdaaTAs4MR6sGYkTByNF\"\n}\n```\n\nReturns human-readable transaction information, parsed from the `getTransaction` method of the Solana RPC.\n</details>\n\n<details>\n<summary>/getTokenAccounts { address }</summary>\n\nReturns the token accounts owned by a user with an amount > 0. Derived from the `getTokenAccountsByOwner` method on the Solana RPC.\n\n</details>\n\n<details>\n<summary>/getSignaturesForAddress { address } </summary>\n\nReturns the transaction signatures returned in `getSignaturesForAddress` method from the Solana RPC.\n\n</details>\n\n\n<details>\n<summary>\n/getTotalValue { address }\n</summary>\n\nReturns the total value of the assets owned by `address`, broken down by NFTs and tokens. Token prices and NFT price estimates are provided by HelloMoon. An example output is provided below\n\n```json\n{\n  \"total\": \"50.00\",\n  \"nftTotal\": \"25.00\",\n  \"tokenTotal\": \"25.00\"\n}\n```\n</details>\n\n### Endpoints for NFT discovery \nThese endpoints are under development and subject to rapid change. These currently use the [Hyperspace API](https://docs.hyperspace.xyz).\n\n<details>\n<summary>/getCollectionsByFloorPrice { maxFloorPrice, minFloorPrice, orderBy, pageSize } </summary>\n\nReturns\n```json\n{\n  \"projects\": [\n    {\n      \"id\": \"<hyperspace-collection-id or pubkey>\",\n      \"desc\": \"collection description\",\n      \"img\": \"collection image url\",\n      \"website\": \"collection website url\",\n      \"floor_price\": 0.1\n    }\n  ],\n  \"hasMore\": true,\n  \"currentPage'\": 1\n}\n```\n</details>\n\n<details>\n<summary>/getListedCollectionNFTs { projectId, pageSize, priceOrder }</summary>\n\nReturns LLM friendly response of available NFTs:\n```json\n{ \n  \"listings\": [\n    {\n      \"price\": 0.1,\n      \"token\": \"<token-address-pubkey>\",\n      \"marketplace\": \"<marketplace-pubkey>\"\n    }\n  ],\n  \"hasMore\": true,\n  \"currentPage\": 1\n} \n```\n</details>\n\n\n## Private endpoints (not LLM accessible)\n\n### Endpoints for Sending Transactions\n\nNote: these endpoints are currently disabled in the production version of the ChatGPT plugin\n\n<details>\n<summary> /createBuyTransaction { token, price }</summary>\n\nRight now we are trusting Hyperspace to craft a valid transaction for us. \nIn the future we will setup a write interface for programs on Solana to adhere to in order to \nbe a target of LLM transaction composition.\n\nReturns\n```json\n{\n  \"linkToSign\": \"<url-to-sign-transaction>\" \n}\n```\n</details>\n\n<details>\n<summary> /createTransferSol { destination, amount }</summary>\n\nCreates a transaction to transfer an amount in Sol.\n\nReturns\n```json\n{\n  \"linkToSign\": \"<url-to-sign-transaction>\" \n}\n```\n</details>\n\n<details>\n<summary> /createTransferToken { destination, mint, amount }</summary>\n\nCreates a transaction to transfer an amount of token (from the mint).\n\nReturns\n```json\n{\n  \"linkToSign\": \"<url-to-sign-transaction>\" \n}\n```\n</details>\n\n### Endpoints for Transaction Composition\n\nThese are also subject to change, and we may create actual webpages to inspect\nthe transaction before signing. However for now, these are simply redirect links \nto ensure that SolanaPay QR codes show up in the ChatGPT link previews.\n\n<details>\n<summary>/page/:methodName</summary>\n\nReturns a webpage with [OpenGraph](https://ogp.me/) metadata that will be rendered in the ChatGPT \nrich link preview. All ChatGPT links should be proxied through this sort of pipeline to maximize\nuser engagement of links. The `og:image` tag is to `/qr/:methodName` to show a SolanaPay QR code in link previews.\n\nThis is currently a blank page, but we may show a preview of the transaction in the future.\n</details>\n\n<details>\n<summary>/qr/:methodName</summary>\n\nReturns a PNG QR code that has been optimized to show in the particular aspect ratio of ChatGPT plugins. \nThis just encodes a SolanaPay link that redirects to `/sign/:methodName`. \n</details>\n\n<details>\n<summary>/sign/:methodName</summary>\n\nThis is the final redirect link that actually returns transaction bytes in a SolanaPay compatible format\nso users can sign transactions that are recommended by ChatGPT.\n\n```json\n{\n  \"transaction\": \"<base64-encoded-transaction-bytes>\"\n}\n```\n</details>\n\n## Development\n\nTo install dependencies, just execute `yarn`. This project uses `node` with version `>=16.17.0`.\n\nTo start a development server, execute `yarn dev`. This will start the plugin available from `localhost:3333` with its own configuration settings in `.well-known-dev/`.\n\n# License\n\nThis codebase is released under [Apache License 2.0](LICENSE.md).\n\n# Disclaimer\n\nBy accessing or using this codebase or any of its components, you accept and agree with the [Disclaimer](DISCLAIMER.md).\n"
  },
  {
    "path": "chatgpt-plugin/.gitignore",
    "content": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pnp\n.pnp.js\n\n# testing\n/coverage\n\n# next.js\n/.next/\n/out/\n\n# production\n/build\n\n# misc\n.DS_Store\n*.pem\n\n# debug\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# local env files\n.env*.local\n\n# vercel\n.vercel\n\n# typescript\n*.tsbuildinfo\nnext-env.d.ts\n"
  },
  {
    "path": "chatgpt-plugin/.prettierrc",
    "content": "{\n  \"tabWidth\": 2,\n  \"useTabs\": false,\n  \"singleQuote\": false,\n  \"printWidth\": 100,\n  \"trailingComma\": \"all\",\n  \"arrowParens\": \"avoid\",\n  \"endOfLine\": \"auto\",\n  \"proseWrap\": \"always\"\n}\n"
  },
  {
    "path": "chatgpt-plugin/README.md",
    "content": "This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).\n\n## Getting Started\n\nFirst, run the development server:\n\n```bash\nnpm run dev\n# or\nyarn dev\n# or\npnpm dev\n```\n\nOpen [http://localhost:3000](http://localhost:3000) with your browser to see the result.\n\nYou can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.\n\nThis project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.\n\n## Learn More\n\nTo learn more about Next.js, take a look at the following resources:\n\n- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.\n- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.\n\nYou can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!\n\n## Deploy on Vercel\n\nThe easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.\n\nCheck out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.\n"
  },
  {
    "path": "chatgpt-plugin/next.config.js",
    "content": "/** @type {import('next').NextConfig} */\nconst nextConfig = {\n  async rewrites() {\n    return [\n      // temporary until Sidekick has plugin autoselect\n      {\n        source: \"/api/helloMoon/defi/tokenName\",\n        destination: \"/api/handlers/tokenName\",\n      },\n    ];\n  },\n};\n\nmodule.exports = nextConfig;\n"
  },
  {
    "path": "chatgpt-plugin/postcss.config.js",
    "content": "module.exports = {\n  plugins: {\n    tailwindcss: {},\n    autoprefixer: {},\n  },\n}\n"
  },
  {
    "path": "chatgpt-plugin/public/openapi.yaml",
    "content": "openapi: 3.0.2\ninfo:\n  title: Solana Labs API\n  description: An API for retrieving human-readable information about the Solana blockchain.\n  version: 1.0.0\npaths:\n  /api/handlers/getAssetsByOwner:\n    post:\n      summary: getAssetsByOwner\n      description: Accepts Solana publicKey address. Returns Metaplex NFTs owned by the address\n      operationId: query_assets_by_owner\n      requestBody:\n        content:\n          application/json:\n            schema:\n              $ref: \"#/components/schemas/getAccountInfoRequest\"\n        required: true\n      responses:\n        \"200\":\n          description: Successful Response\n        \"500\":\n          description: Validation Error\n      security:\n        - HTTPBearer: []\n  /api/handlers/getAccountInfo:\n    post:\n      summary: getAccountInfo\n      description:\n        Returns information about the data stored by that account in a human-readable format.\n        Human-readable formatting is only possible when the account's corresponding program owner\n        has published an Anchor IDL on the Solana blockchain.\n      operationId: query_account_info\n      requestBody:\n        content:\n          application/json:\n            schema:\n              $ref: \"#/components/schemas/getAccountInfoRequest\"\n        required: true\n      responses:\n        \"200\":\n          description: Successful Response\n        \"500\":\n          description: Validation Error\n      security:\n        - HTTPBearer: []\n  /api/handlers/getTokenAccounts:\n    post:\n      summary: getTokenAccounts\n      description:\n        Returns the fungible and non-fungible tokens and amounts owned by the address. May show\n        tokens not listed in get_assets_by_owner.\n      operationId: query_token_accounts\n      requestBody:\n        content:\n          application/json:\n            schema:\n              $ref: \"#/components/schemas/getBalanceRequest\"\n        required: true\n      responses:\n        \"200\":\n          description: Successful Response\n        \"500\":\n          description: Validation Error\n      security:\n        - HTTPBearer: []\n  /api/handlers/getBalance:\n    post:\n      summary: getBalance\n      description:\n        Accepts Solana publicKey address. Returns the amount of lamports that the account has\n        available.\n      operationId: query_balance\n      requestBody:\n        content:\n          application/json:\n            schema:\n              $ref: \"#/components/schemas/getBalanceRequest\"\n        required: true\n      responses:\n        \"200\":\n          description: Successful Response\n        \"500\":\n          description: Validation Error\n      security:\n        - HTTPBearer: []\n  /api/handlers/getTransaction:\n    post:\n      summary: getTransaction\n      description:\n        Accepts a transaction signature. Returns the publicly available transaction information and\n        metadata. Only high level summaries based on instruction data should be provided to users\n        unless otherwise specified. Logs, compute units, and fees are available in devMode.\n      operationId: query_transaction\n      requestBody:\n        content:\n          application/json:\n            schema:\n              $ref: \"#/components/schemas/getTransactionRequest\"\n      responses:\n        \"200\":\n          description: Successful Response\n        \"500\":\n          description: Validation Error\n      security:\n        - HTTPBearer: []\n  /api/handlers/getSignaturesForAddress:\n    post:\n      summary: getSignaturesForAddress\n      description:\n        Accepts Solana publicKey address. Returns the latest transaction signatures that involve\n        that address.\n      operationId: query_signatures_for_address\n      requestBody:\n        content:\n          application/json:\n            schema:\n              $ref: \"#/components/schemas/getSignaturesForAddressRequest\"\n        required: true\n      responses:\n        \"200\":\n          description: Successful Response\n        \"500\":\n          description: Validation Error\n      security:\n        - HTTPBearer: []\n  /api/handlers/getCollectionsByFloorPrice:\n    post:\n      summary: Search through Solana NFT collections by floor price\n      operationId: query_nft_collections_by_fp\n      requestBody:\n        content:\n          application/json:\n            schema:\n              $ref: \"#/components/schemas/getCollectionsByFloorPriceRequest\"\n      responses:\n        \"200\":\n          description: Successful Response\n        \"500\":\n          description: Validation Error\n      security:\n        - HTTPBearer: []\n  /api/handlers/getListedCollectionNFTs:\n    post:\n      summary: Returns the listed NFTs in a collection available to purchase\n      operationId: query_listed_nfts_for_collection\n      requestBody:\n        content:\n          application/json:\n            schema:\n              $ref: \"#/components/schemas/getListedCollectionNFTsRequest\"\n      responses:\n        \"200\":\n          description: Successful Response\n        \"500\":\n          description: Validation Error\n      security:\n        - HTTPBearer: []\n  /api/handlers/getCollectionsByName:\n    post:\n      summary: Searches for NFT collections based on project name\n      operationId: query_nfts_collections_by_name\n      requestBody:\n        content:\n          application/json:\n            schema:\n              $ref: \"#/components/schemas/getCollectionsByNameRequest\"\n      responses:\n        \"200\":\n          description: Successful Response\n        \"500\":\n          description: Validation Error\n      security:\n        - HTTPBearer: []\n  /api/handlers/getTotalValue:\n    post:\n      summary:\n        Calculates the total value of a the address's holdings in USD, with breakdown by NFTs and\n        tokens.\n      operationId: query_total_value\n      requestBody:\n        content:\n          application/json:\n            schema:\n              $ref: \"#/components/schemas/getTotalValueRequest\"\n      responses:\n        \"200\":\n          description: Successful Response\n        \"500\":\n          description: Validation Error\n      security:\n        - HTTPBearer: []\n  /api/handlers/walletName:\n    post:\n      summary:\n        Resolves wallet names to the actual solana address, or if it is already a Solana address, it\n        looks up all the wallet names associated with that publickey. This works especially well for\n        .sol, .glow, .backpack, or .poor domains.\n      operationId: query_wallet_name\n      requestBody:\n        content:\n          application/json:\n            schema:\n              type: object\n              properties:\n                walletName:\n                  type: string\n      responses:\n        \"200\":\n          description: Successful Response\n        \"500\":\n          description: Validation Error\n      security:\n        - HTTPBearer: []\n  /api/handlers/tokenName:\n    post:\n      summary:\n        Searches tokens by name, and returns the mint address, logo, name and symbol for up to 10\n        relevant, verified tokens.\n      operationId: search_token_name\n      requestBody:\n        content:\n          application/json:\n            schema:\n              type: object\n              properties:\n                tokenName:\n                  type: string\n      responses:\n        \"200\":\n          description: Successful Response\n        \"500\":\n          description: Validation Error\n      security:\n        - HTTPBearer: []\ncomponents:\n  schemas:\n    getAccountInfoRequest:\n      type: object\n      required:\n        - address\n      properties:\n        address:\n          type: string\n          description: Base58 encoded PublicKey\n    getSignaturesForAddressRequest:\n      title: GetSignaturesForAddressRequest\n      type: object\n      required:\n        - address\n      properties:\n        address:\n          type: string\n          description: Base58 encoded PublicKey\n        beforeSignature:\n          type: string\n        untilSignature:\n          type: string\n    getBalanceRequest:\n      title: GetBalanceRequest\n      type: object\n      required:\n        - address\n      properties:\n        address:\n          title: Address\n          type: string\n          description: Base58 encoded PublicKey\n    getTransactionRequest:\n      type: object\n      required:\n        - signature\n      properties:\n        signature:\n          type: string\n        devMode:\n          type: boolean\n    getCollectionsByFloorPriceRequest:\n      type: object\n      properties:\n        maxFloorPrice:\n          type: number\n          nullable: true\n        minFloorPrice:\n          type: number\n          nullable: true\n        orderBy:\n          type: string\n          enum:\n            - ASC\n            - DESC\n          nullable: true\n    getCollectionsByNameRequest:\n      type: object\n      properties:\n        projectName:\n          type: string\n    getListedCollectionNFTsRequest:\n      type: object\n      properties:\n        projectId:\n          type: string\n        priceOrder:\n          type: string\n          nullable: true\n        pageSize:\n          type: number\n          nullable: true\n    getTotalValueRequest:\n      type: object\n      properties:\n        address:\n          type: string\n          description: Base58 encoded PublicKey\n  securitySchemes:\n    HTTPBearer:\n      type: http\n      scheme: bearer\n"
  },
  {
    "path": "chatgpt-plugin/src/app/globals.css",
    "content": "@tailwind base;\n@tailwind components;\n\n:root {\n  --foreground-rgb: 255, 255, 255;\n  --background-start-rgb: 0, 0, 0;\n  --background-end-rgb: 0, 0, 0;\n}\n\n\nbody {\n  color: rgb(var(--foreground-rgb));\n  background: linear-gradient(to bottom, transparent, rgb(var(--background-end-rgb)))\n    rgb(var(--background-start-rgb));\n}\n\n.ms-6 {\n  margin-left: 2rem !important;\n}\nimg,\nsvg {\n  vertical-align: middle;\n}\n@media (min-width: 768px) {\n  .AiHero_hero__title__ZatKJ {\n    font-size: 6rem;\n    line-height: 6rem;\n    margin-bottom: 32px !important;\n  }\n}\n.AiHero_hero__title__ZatKJ {\n  font-weight: 700;\n  font-size: 3.5rem;\n  margin-bottom: 24px !important;\n}\nh2 {\n  font-size: 2.425rem;\n  line-height: 3.15rem;\n  font-weight: 400;\n}\n.czAcYK.btn {\n  font-family: DSemi, monospace;\n  font-weight: normal;\n  text-transform: uppercase;\n  line-height: 1.25rem;\n  font-size: 0.95rem;\n  width: fit-content;\n  background-color: rgb(153, 69, 255);\n  border-radius: 2.25rem;\n  padding: 0.875rem 1.5rem;\n  color: white;\n  border: 1px solid transparent;\n}\n\n.lift {\n  transition: box-shadow 0.25s ease, transform 0.25s ease;\n}\n\n.btn {\n  background-color: rgb(153, 69, 255);\n  @apply flex items-center gap-3 px-5 py-3 text-lg transition transform whitespace-nowrap rounded-3xl;\n  @apply hover:bg-white hover:text-black;\n  @apply hover:-translate-y-1;\n}\n.btn svg {\n  @apply w-5 h-5;\n}\n\n.disclosure {\n  @apply flex items-center justify-between w-full px-4 py-2 rounded-xl;\n  @apply text-white bg-gray-800;\n  @apply hover:bg-opacity-80;\n  @apply border border-gray-700;\n}\n.disclosure-open {\n  @apply font-semibold;\n  @apply bg-gray-800 border-gray-600 hover:bg-opacity-100;\n}\n\n.disclosure svg {\n  @apply w-5 h-5;\n}\n\n/* utilities at the end to make the inline classes a priority */\n@tailwind utilities;\n\n.legal {\n  text-decoration: underline;\n  color: #00ffbd;\n}"
  },
  {
    "path": "chatgpt-plugin/src/app/layout.tsx",
    "content": "import Link from \"next/link\";\nimport \"./globals.css\";\nimport { Inter } from \"next/font/google\";\nimport Image from \"next/image\";\n\nconst inter = Inter({ subsets: [\"latin\"] });\n\nexport const metadata = {\n  title: \"Solana Labs - ChatGPT Plugin\",\n  description: \"Get a taste of Solana\",\n};\n\nexport default function RootLayout({ children }: { children: React.ReactNode }) {\n  return (\n    <html lang=\"en\">\n      <body className={inter.className}>\n        <nav className=\"flex items-center p-4\">\n          <Link href={\"/\"}>\n            <Image src=\"/solana-image.svg\" width=\"149\" height=\"22\" alt=\"Solana\" />\n          </Link>\n\n          <section className=\"\">{/* add header links in here, if desired */}</section>\n        </nav>\n\n        {children}\n\n        <footer className=\"my-8 text-center text-gray-400\">\n          &copy;\n          {new Date().getFullYear()}{\" \"}\n          <Link href=\"https://solanalabs.com\" className=\"hover:underline hover:text-white\">\n            Solana Labs\n          </Link>\n          {\" | \"}\n          <Link href=\"https://solanapay.com/tos\" className=\"hover:underline hover:text-white\">\n            Terms of Service\n          </Link>\n        </footer>\n      </body>\n    </html>\n  );\n}\n"
  },
  {
    "path": "chatgpt-plugin/src/app/page.tsx",
    "content": "\"use client\";\n\nimport Head from \"next/head\";\nimport Link from \"next/link\";\n\nimport { features } from \"@/lib/features\";\nimport { Disclosure } from \"@headlessui/react\";\nimport { ArrowRightIcon, ChevronDownIcon, ChevronUpIcon } from \"@heroicons/react/24/outline\";\n\nexport default function Home() {\n  return (\n    <>\n      <Head>\n        <title>Solana Labs - ChatGPT Plugin</title>\n      </Head>\n\n      <main className=\"block max-w-2xl p-4 mx-auto \">\n        <section className=\"space-y-1 text-center\">\n          <h1 className=\"text-6xl font-semibold \">\n            <Link href=\"/\">Solana Labs</Link>\n          </h1>\n          <h2 className=\"text-2xl font-normal\">ChatGPT Plugin</h2>\n        </section>\n\n        <p className=\"my-10 text-center\">\n          The Solana Labs ChatGPT Plugin helps you query information on the Solana blockchain. Solana Labs has provided the code as a reference implementation with permissive\n          licensing on Github. A list of features and example queries is provided below.\n        </p>\n\n        <div className=\"flex items-center justify-center gap-5 my-3\">\n          <Link\n            target=\"_blank\"\n            href={\"https://github.com/solana-labs/chatgpt-plugin\"}\n            className=\"btn\"\n          >\n            GitHub Repo\n            <ArrowRightIcon />\n          </Link>\n\n          <Link target=\"_blank\" href={\"https://solana.com/ai\"} className=\"btn\">\n            AI Grants\n            <ArrowRightIcon />\n          </Link>\n        </div>\n\n        <section className=\"my-10 space-y-6\">\n          <h2 className=\"text-3xl ml-3\">Features</h2>\n          {features.map((endpoint, id) => (\n            <Disclosure key={id}>\n              {({ open }) => (\n                <div className=\"\">\n                  <Disclosure.Button className={`disclosure ${open ? \"disclosure-open\" : \"\"}`}>\n                    {endpoint.label}\n                    <span className=\"\">{open ? <ChevronUpIcon /> : <ChevronDownIcon />}</span>\n                  </Disclosure.Button>\n                  <Disclosure.Panel className=\"px-3 py-2 text-white\">\n                    {endpoint.description}\n                  </Disclosure.Panel>\n                </div>\n              )}\n            </Disclosure>\n          ))}\n        </section>\n      </main>\n    </>\n  );\n}\n"
  },
  {
    "path": "chatgpt-plugin/src/lib/address.ts",
    "content": "import {\n  walletAddressToDotAnything,\n  walletAddressToDotGlow,\n  walletAddressToDotBackpack,\n  walletNameToAddressAndProfilePicture,\n} from \"@portal-payments/solana-wallet-names\";\nimport { Connection, PublicKey } from \"@solana/web3.js\";\nimport { Client } from \"@solflare-wallet/utl-sdk\";\nimport configConstants, { CONNECTION } from \"../pages/api/constants\";\nconfigConstants();\n\nexport interface WalletNames {\n  walletNames: string[];\n}\n\nexport const walletAddressToDotSolCustom = async (\n  connection: Connection,\n  wallet: PublicKey,\n): Promise<WalletNames> => {\n  try {\n    const result = await fetch(\n      // See https://github.com/Bonfida/sns-sdk#sdk-proxy\n      // There's a 'favorite-domain' endpoint butmost SNS users haven't set up a\n      // favorite domain, as the UI to do so is complex\n      // `https://sns-sdk-proxy.bonfida.workers.dev/favorite-domain/${wallet.toBase58()}`\n      `https://sns-sdk-proxy.bonfida.workers.dev/domains/${wallet.toBase58()}`,\n      {\n        method: \"GET\",\n      },\n    );\n\n    const body = await result.json();\n    let walletNames: string[] = body.result.map((info: any) => `${info.domain}.sol`);\n    return {\n      walletNames,\n    };\n  } catch (thrownObject) {\n    const error = thrownObject as Error;\n    if (error.message === \"Invalid wallet account provided\") {\n      return {\n        walletNames: [],\n      };\n    }\n    throw error;\n  }\n};\n\nexport const walletAddressToNameAndProfilePictureCustom = async (\n  connection: Connection,\n  wallet: PublicKey,\n  backpackJWT: string | null = null,\n): Promise<WalletNames> => {\n  let walletNames: string[] = [];\n  const dotAnything = await walletAddressToDotAnything(connection, wallet);\n  if (dotAnything.walletName) {\n    walletNames.push(dotAnything.walletName);\n  }\n\n  const dotSol = await walletAddressToDotSolCustom(connection, wallet);\n  walletNames = walletNames.concat(dotSol.walletNames);\n\n  const dotGlow = await walletAddressToDotGlow(wallet);\n  if (dotGlow?.walletName && dotGlow.walletName !== \"null.glow\") {\n    walletNames.push(dotGlow.walletName);\n  }\n\n  if (backpackJWT) {\n    const dotBackpack = await walletAddressToDotBackpack(wallet, backpackJWT);\n    if (dotBackpack?.walletName) {\n      walletNames.push(dotBackpack.walletName);\n    }\n  }\n\n  return {\n    walletNames,\n  };\n};\n\nexport async function resolveAddress(address: string): Promise<PublicKey> {\n  if (!address) {\n    throw new Error(`No address provided for: ${address}`);\n  }\n\n  if (address.search(/\\./g) !== -1) {\n    let info: { walletAddress: string | null };\n    try {\n      info = await walletNameToAddressAndProfilePicture(CONNECTION, address);\n    } catch (e) {\n      throw new Error(`Wallet name ${address} does not exist or cannot be resolved.`);\n    }\n\n    if (info.walletAddress === null) {\n      throw new Error(`Wallet name does not exist: ${address}`);\n    }\n    return new PublicKey(info.walletAddress);\n  }\n\n  try {\n    return new PublicKey(address);\n  } catch (_e) {\n    throw new Error(`Provided address is invalid base58 Solana address: ${address}`);\n  }\n}\n\nexport async function resolveToken(tokenName: string): Promise<PublicKey> {\n  if (!tokenName) {\n    throw new Error(`No token name provided for: ${tokenName}`);\n  }\n\n  const utl = new Client();\n  try {\n    let results = (await utl.searchMints(tokenName))\n      .filter((res: any) => {\n        return res[\"verified\"] && res[\"holders\"] > 0 && res[\"chainId\"] === 101;\n      })\n      .map((res: any) => {\n        return {\n          mintAddress: res[\"address\"],\n          tokenName: res[\"name\"],\n          tokenSymbol: res[\"symbol\"],\n          holders: res[\"holders\"],\n          verified: res[\"verified\"],\n          chainId: res[\"chainId\"],\n          logoURI: res[\"logoURI\"],\n        };\n      });\n    return new PublicKey(results[0].mintAddress);\n  } catch (_e) {\n    throw new Error(\n      `Provided token address does not have any relevant token addresses: ${tokenName}`,\n    );\n  }\n}\n"
  },
  {
    "path": "chatgpt-plugin/src/lib/features.tsx",
    "content": "import Link from \"next/link\";\n\nexport type Endpoint = {\n  label: string;\n  description: React.ReactNode;\n};\n\nexport const features: Array<Endpoint> = [\n  {\n    label: \"Fetch account balance\",\n    description: (\n      <p>\n        {`Example: \"What is the balance of `}\n        <i>MY-WALLET-ADDRESS</i>\n        {` ?\"`}\n        <br /> <br />\n        {`Returns the amount of Sol that the wallet address owns.`}\n      </p>\n    ),\n  },\n  {\n    label: \"Interpret Solana accounts\",\n    description: (\n      <p>\n        {`Example: \"What data is in `}\n        <i>ACCOUNT-ADDRESS</i>\n        {` ?\"`}\n        <br /> <br />\n        {`Returns data about the account, including the owner, the amount of Sol, and the potentially human-readable data stored in the account, if it's owner program has an Anchor IDL.`}\n      </p>\n    ),\n  },\n  {\n    label: \"Interpret Solana transactions\",\n    description: (\n      <p>\n        {`Example: \"What happened in transaction `}\n        <i>TRANSACTION-ID</i>\n        {` ?\"`}\n        <br /> <br />\n        {`Returns information about the transaction, and potentially human readable information about program instructions, if the programs have Anchor IDLs.`}\n      </p>\n    ),\n  },\n  {\n    label: \"Find NFTs for a wallet\",\n    description: (\n      <p>\n        {`Example: \"What NFTs does `}\n        <i>MY-WALLET-ADDRESS</i>\n        {` own?\"`}\n        <br /> <br />\n        {`The plugin can list your NFTs and their metadata. If you have a lot of NFTs, you can ask it to list all of your NFTs 5 at a time.`}\n      </p>\n    ),\n  },\n  {\n    label: \"Inspect wallet activity\",\n    description: (\n      <p>\n        {`Example: \"What are the latest transactions for `}\n        <i>WALLET-ADDRESS</i>\n        {` ?\"`}\n        <br /> <br />\n        {`The plugin can help you go through wallet transactions and explore on-chain activity.`}\n      </p>\n    ),\n  },\n  {\n    label: \"Search NFT collections by name or floor price\",\n    description: (\n      <p>\n        {`Example: \"Can you find me the cheapest Mad Lads NFT for sale?\"`}\n        <br /> <br />\n        {`NFTs are searched by name and sorted by floor price, you can ask the model to search explicitly by either target floor price name or by name.\n        NFTs within a collection can also be sorted by floor price.`}\n        <br />\n        <br />\n        <u>\n          <b>Note:</b>\n        </u>\n        {` If you cannot find your NFT Collection, please see if it exists on `}\n        <u>\n          <Link href=\"https://hyperspace.xyz\">hyperspace.xyz</Link>\n        </u>\n        {`. We\n        use Hyperspace's API to support this plugin`}\n      </p>\n    ),\n  },\n  {\n    label: \"Calculate wallet value\",\n    description: (\n      <p>\n        {`Example: \"What is the value of `}\n        <i>MY-ADDRESS</i>\n        {` ?\"`}\n        <br /> <br />\n        {`Wallet value is calculated by estimating the value of all tokens and NFTs owned by the wallet. Please note that these values are in USD and are subject to change due to market fluctuations. Price estimates are provided by `}\n        <u>\n          <Link href=\"https://hyperspace.xyz\">{`HelloMoon's`}</Link>\n        </u>\n        {` API.`}\n      </p>\n    ),\n  },\n  // {\n  //   label: \"Buy an NFT (SolanaPay)\",\n  //   description: (\n  //     <p>\n  //       {`Example: \"Can you help me transfer 1 EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v to `}\n  //       <i>DESTINATION</i>\n  //       {` ?\"`}\n  //       <br />\n  //       <br />\n  //       This will generate a QR code that you can scan from within your mobile wallet to send 1 USDC\n  //       (mint address EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v) from your wallet to a\n  //       destination wallet. See the{\" \"}\n  //       <Link\n  //         href={\n  //           \"https://github.com/solana-labs/chatgpt-plugin/blob/examples-langchain/DISCLAIMER.md\"\n  //         }\n  //         className=\"legal\"\n  //       >\n  //         Disclaimer\n  //       </Link>\n  //       {\" and \"}\n  //       <Link href=\"https://solanapay.com/tos\" className=\"legal\">\n  //         Terms of Service\n  //       </Link>\n  //       {\".\"}\n  //     </p>\n  //   ),\n  // },\n  // {\n  //   label: \"Transfer Sol (SolanaPay)\",\n  //   description: (\n  //     <p>\n  //       {`Example: \"Can you help me transfer 1 SOL to `}\n  //       <i>DESTINATION</i>\n  //       {` ?\"`}\n  //       <br />\n  //       <br />\n  //       This will generate a QR code that you can scan from within your mobile wallet to send 1 Sol\n  //       from your wallet to a destination wallet address. See the{\" \"}\n  //       <Link\n  //         href={\n  //           \"https://github.com/solana-labs/chatgpt-plugin/blob/examples-langchain/DISCLAIMER.md\"\n  //         }\n  //         className=\"legal\"\n  //       >\n  //         Disclaimer\n  //       </Link>\n  //       {\" and \"}\n  //       <Link href=\"https://solanapay.com/tos\" className=\"legal\">\n  //         Terms of Service\n  //       </Link>\n  //       {\".\"}\n  //     </p>\n  //   ),\n  // },\n  // {\n  //   label: \"Transfer a token (SolanaPay)\",\n  //   description: (\n  //     <p>\n  //       {`Example: \"Can you help me transfer 1 EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v to `}\n  //       <i>DESTINATION</i>\n  //       {` ?\"`}\n  //       <br />\n  //       <br />\n  //       This will generate a QR code that you can scan from within your mobile wallet to send 1 USDC\n  //       (mint address EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v) from your wallet to a\n  //       destination wallet. See the{\" \"}\n  //       <Link\n  //         href={\n  //           \"https://github.com/solana-labs/chatgpt-plugin/blob/examples-langchain/DISCLAIMER.md\"\n  //         }\n  //         className=\"legal\"\n  //       >\n  //         Disclaimer\n  //       </Link>\n  //       {\" and \"}\n  //       <Link href=\"https://solanapay.com/tos\" className=\"legal\">\n  //         Terms of Service\n  //       </Link>\n  //       {\".\"}\n  //     </p>\n  //   ),\n  // },\n];\n"
  },
  {
    "path": "chatgpt-plugin/src/lib/helloMoon/index.ts",
    "content": "export const VALID_OPERATORS = [\"=\", \"!=\", \">\", \"<\", \">=\"];\nexport type Comparison =\n  | {\n      operator: \"=\" | \"!=\" | \">\" | \"<\" | \">=\";\n      value: number;\n    }\n  | undefined;\n\nexport function buildComparison(\n  operator: string | undefined,\n  value: number | undefined,\n): Comparison {\n  if (operator && !VALID_OPERATORS.includes(operator)) {\n    throw new Error(\n      \"Invalid operator: \" +\n        operator +\n        \". Valid operators are: \" +\n        VALID_OPERATORS.join(\", \") +\n        \".\",\n    );\n  }\n\n  const comparison =\n    operator && value !== undefined\n      ? {\n          operator,\n          value: new Number(value).valueOf(),\n        }\n      : undefined;\n\n  return comparison as Comparison;\n}\n\n/**\n * Rewrites a \"B - A\" pair to \"A-B\" pair\n * @param pair\n */\nexport function cleanSwapPair(pair: string) {\n  let cleaned = pair.replaceAll(\" \", \"\");\n  let split = cleaned.split(\"-\");\n  let pairA = split[0];\n  let pairB = split[1];\n  if (pairA < pairB) {\n    return `${pairA} - ${pairB}`;\n  }\n  return `${pairB} - ${pairA}`;\n}\n"
  },
  {
    "path": "chatgpt-plugin/src/lib/hyperspace/account.ts",
    "content": "import { Keypair, PublicKey } from \"@solana/web3.js\";\nimport * as anchor from \"@coral-xyz/anchor\";\nimport {\n  HYPERSPACE,\n  FEE_PAYER,\n  HYPERSPACE_ID as MARKETPLACE_PROGRAM_ID,\n} from \"./constants\";\nimport { hyperspaceIdl } from \"./idl/hyperspace\";\nimport { AnchorProvider } from \"@coral-xyz/anchor\";\nimport {\n  TOKEN_PROGRAM_ID,\n  ASSOCIATED_TOKEN_PROGRAM_ID as SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID,\n} from \"@solana/spl-token\";\nimport { PROGRAM_ID as TOKEN_METADATA_PROGRAM_ID } from \"@metaplex-foundation/mpl-token-metadata\";\n\nexport async function loadHyperspaceProgram(\n  anchorWallet: any,\n  connection: anchor.web3.Connection\n) {\n  const provider = new AnchorProvider(connection, anchorWallet, {\n    preflightCommitment: \"recent\",\n  });\n  return new anchor.Program(hyperspaceIdl, MARKETPLACE_PROGRAM_ID, provider);\n}\n\nexport const getHyperspaceBuyerEscrow = async (\n  auctionHouse: anchor.web3.PublicKey,\n  wallet: anchor.web3.PublicKey\n): Promise<[PublicKey, number]> => {\n  return await anchor.web3.PublicKey.findProgramAddress(\n    [Buffer.from(HYPERSPACE), auctionHouse.toBuffer(), wallet.toBuffer()],\n    MARKETPLACE_PROGRAM_ID\n  );\n};\n\nexport const getHyperspaceTradeState = async (\n  is_buy: boolean,\n  wallet: anchor.web3.PublicKey,\n  tokenAccount: anchor.web3.PublicKey,\n  tokenMint: anchor.web3.PublicKey,\n  tokenSize: anchor.BN\n): Promise<[PublicKey, number]> => {\n  return await anchor.web3.PublicKey.findProgramAddress(\n    [\n      Buffer.from(HYPERSPACE),\n      is_buy\n        ? new anchor.BN(1).toArrayLike(Buffer, \"le\", 1)\n        : new anchor.BN(0).toArrayLike(Buffer, \"le\", 1),\n      wallet.toBuffer(),\n      tokenAccount.toBuffer(),\n      tokenMint.toBuffer(),\n      tokenSize.toArrayLike(Buffer, \"le\", 8),\n    ],\n    MARKETPLACE_PROGRAM_ID\n  );\n};\n\nexport const getMetadata = async (\n  mint: anchor.web3.PublicKey\n): Promise<anchor.web3.PublicKey> => {\n  return (\n    await anchor.web3.PublicKey.findProgramAddress(\n      [\n        Buffer.from(\"metadata\"),\n        TOKEN_METADATA_PROGRAM_ID.toBuffer(),\n        mint.toBuffer(),\n      ],\n      TOKEN_METADATA_PROGRAM_ID\n    )\n  )[0];\n};\n\nexport const getHyperspace = async (\n  creator: anchor.web3.PublicKey\n): Promise<[PublicKey, number]> => {\n  return await anchor.web3.PublicKey.findProgramAddress(\n    [Buffer.from(HYPERSPACE), creator.toBuffer()],\n    MARKETPLACE_PROGRAM_ID\n  );\n};\n\nexport const getHyperspaceFeeAcct = async (\n  auctionHouse: anchor.web3.PublicKey\n): Promise<[PublicKey, number]> => {\n  return await anchor.web3.PublicKey.findProgramAddress(\n    [Buffer.from(HYPERSPACE), auctionHouse.toBuffer(), Buffer.from(FEE_PAYER)],\n    MARKETPLACE_PROGRAM_ID\n  );\n};\n\nexport const getAtaForMint = async (\n  mint: anchor.web3.PublicKey,\n  buyer: anchor.web3.PublicKey\n): Promise<[anchor.web3.PublicKey, number]> => {\n  return await anchor.web3.PublicKey.findProgramAddress(\n    [buyer.toBuffer(), TOKEN_PROGRAM_ID.toBuffer(), mint.toBuffer()],\n    SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID\n  );\n};\n\nexport const getEditionDataAccount = async (\n  mint: anchor.web3.PublicKey\n): Promise<[anchor.web3.PublicKey, number]> => {\n  return await anchor.web3.PublicKey.findProgramAddress(\n    [\n      Buffer.from(\"metadata\"),\n      TOKEN_METADATA_PROGRAM_ID.toBuffer(),\n      mint.toBuffer(),\n      Buffer.from(\"edition\"),\n    ],\n    TOKEN_METADATA_PROGRAM_ID\n  );\n};\n\nexport const getHyperspaceProgramAsSigner = async (): Promise<\n  [PublicKey, number]\n> => {\n  return await anchor.web3.PublicKey.findProgramAddress(\n    [Buffer.from(HYPERSPACE), Buffer.from(\"signer\")],\n    MARKETPLACE_PROGRAM_ID\n  );\n};\n\nexport const findTokenRecordPda = (\n  mint: PublicKey,\n  token: PublicKey\n): PublicKey => {\n  return PublicKey.findProgramAddressSync(\n    [\n      Buffer.from(\"metadata\"),\n      TOKEN_METADATA_PROGRAM_ID.toBuffer(),\n      mint.toBuffer(),\n      Buffer.from(\"token_record\"),\n      token.toBuffer(),\n    ],\n    TOKEN_METADATA_PROGRAM_ID\n  )[0];\n};\n"
  },
  {
    "path": "chatgpt-plugin/src/lib/hyperspace/constants.ts",
    "content": "import { PublicKey } from \"@solana/web3.js\";\n\nexport const FEE_PAYER = \"fee_payer\";\nexport const HYPERSPACE = \"hyperspace\";\n\nexport const HYPERSPACE_ID = new PublicKey(\n  \"HYPERfwdTjyJ2SCaKHmpF2MtrXqWxrsotYDsTrshHWq8\"\n);\n\nexport const HYPERSPACE_MARKETPLACE_INSTANCE_ID_STRING =\n  \"5pdaXth4ijgDCeYDKgSx3jAbN7m8h4gy1LRCErAAN1LM\";\nexport const HYPERSPACE_MARKETPLACE_INSTANCE = new PublicKey(\n  \"5pdaXth4ijgDCeYDKgSx3jAbN7m8h4gy1LRCErAAN1LM\"\n);\n"
  },
  {
    "path": "chatgpt-plugin/src/lib/hyperspace/idl/hyperspace.ts",
    "content": "export type Hyperspace = {\n  version: \"0.1.0\";\n  name: \"hyperspace\";\n  instructions: [\n    {\n      name: \"initProgramAsSigner\";\n      accounts: [\n        {\n          name: \"wallet\";\n          isMut: true;\n          isSigner: true;\n        },\n        {\n          name: \"programAsSigner\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"systemProgram\";\n          isMut: false;\n          isSigner: false;\n        }\n      ];\n      args: [\n        {\n          name: \"programAsSignerBump\";\n          type: \"u8\";\n        }\n      ];\n    },\n    {\n      name: \"updateHyperspace\";\n      accounts: [\n        {\n          name: \"payer\";\n          isMut: false;\n          isSigner: true;\n        },\n        {\n          name: \"authority\";\n          isMut: false;\n          isSigner: true;\n        },\n        {\n          name: \"newAuthority\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"feeWithdrawalDestination\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"treasuryWithdrawalDestination\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"treasuryWithdrawalDestinationOwner\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"hyperspace\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"tokenProgram\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"systemProgram\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"ataProgram\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"rent\";\n          isMut: false;\n          isSigner: false;\n        }\n      ];\n      args: [\n        {\n          name: \"sellerFeeBasisPoints\";\n          type: {\n            option: \"u16\";\n          };\n        },\n        {\n          name: \"requiresSignOff\";\n          type: {\n            option: \"bool\";\n          };\n        },\n        {\n          name: \"canChangeSalePrice\";\n          type: {\n            option: \"bool\";\n          };\n        }\n      ];\n    },\n    {\n      name: \"withdraw\";\n      accounts: [\n        {\n          name: \"wallet\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"receiptAccount\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"escrowPaymentAccount\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"authority\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"hyperspace\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"tokenProgram\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"systemProgram\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"rent\";\n          isMut: false;\n          isSigner: false;\n        }\n      ];\n      args: [\n        {\n          name: \"escrowPaymentBump\";\n          type: \"u8\";\n        },\n        {\n          name: \"amount\";\n          type: \"u64\";\n        }\n      ];\n    },\n    {\n      name: \"deposit\";\n      accounts: [\n        {\n          name: \"wallet\";\n          isMut: false;\n          isSigner: true;\n        },\n        {\n          name: \"paymentAccount\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"transferAuthority\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"escrowPaymentAccount\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"authority\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"hyperspace\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"hyperspaceFeeAccount\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"tokenProgram\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"systemProgram\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"rent\";\n          isMut: false;\n          isSigner: false;\n        }\n      ];\n      args: [\n        {\n          name: \"escrowPaymentBump\";\n          type: \"u8\";\n        },\n        {\n          name: \"amount\";\n          type: \"u64\";\n        }\n      ];\n    },\n    {\n      name: \"permissionlessCancel\";\n      accounts: [\n        {\n          name: \"wallet\";\n          isMut: false;\n          isSigner: true;\n        },\n        {\n          name: \"tokenMint\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"seller\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"tokenAccount\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"tradeState\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"programAsSigner\";\n          isMut: false;\n          isSigner: false;\n        }\n      ];\n      args: [\n        {\n          name: \"programAsSignerBump\";\n          type: \"u8\";\n        },\n        {\n          name: \"tradeStateBump\";\n          type: \"u8\";\n        },\n        {\n          name: \"tokenSize\";\n          type: \"u64\";\n        }\n      ];\n    },\n    {\n      name: \"collectionCancelBuy\";\n      accounts: [\n        {\n          name: \"wallet\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"identifierPubkey\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"authority\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"hyperspace\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"collectionBuyerTradeState\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"programAsSigner\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"tokenProgram\";\n          isMut: false;\n          isSigner: false;\n        }\n      ];\n      args: [\n        {\n          name: \"buyerPrice\";\n          type: \"u64\";\n        },\n        {\n          name: \"programAsSignerBump\";\n          type: \"u8\";\n        },\n        {\n          name: \"tradeStateBump\";\n          type: \"u8\";\n        },\n        {\n          name: \"collectionTradeStateType\";\n          type: \"u8\";\n        },\n        {\n          name: \"identifierIndex\";\n          type: \"u8\";\n        }\n      ];\n    },\n    {\n      name: \"cancel\";\n      accounts: [\n        {\n          name: \"wallet\";\n          isMut: true;\n          isSigner: false;\n          docs: [\"CHECK OWNER OF TRADE_STATE TODO\"];\n        },\n        {\n          name: \"tokenMint\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"tokenAccount\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"metadata\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"authority\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"hyperspace\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"tradeState\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"tokenProgram\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"systemProgram\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"metadataProgram\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"programAsSigner\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"instructions\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"tokenRecord\";\n          isMut: true;\n          isSigner: false;\n          docs: [\"CHECK no check needed\"];\n        },\n        {\n          name: \"editionAccount\";\n          isMut: false;\n          isSigner: false;\n          docs: [\"CHECK no check needed\"];\n        },\n        {\n          name: \"authorizationRules\";\n          isMut: false;\n          isSigner: false;\n          docs: [\"CHECK no check needed\"];\n        },\n        {\n          name: \"mplTokenAuthRulesProgram\";\n          isMut: false;\n          isSigner: false;\n          docs: [\"CHECK no check needed\"];\n        }\n      ];\n      args: [\n        {\n          name: \"isBuy\";\n          type: \"u8\";\n        },\n        {\n          name: \"programAsSignerBump\";\n          type: \"u8\";\n        },\n        {\n          name: \"tradeStateBump\";\n          type: \"u8\";\n        },\n        {\n          name: \"tokenSize\";\n          type: \"u64\";\n        }\n      ];\n    },\n    {\n      name: \"collectionExecuteSale\";\n      accounts: [\n        {\n          name: \"buyer\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"buyerBrokerWallet\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"seller\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"sellerBrokerWallet\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"tokenAccount\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"tokenMint\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"metadata\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"editionData\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"escrowPaymentAccount\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"buyerReceiptTokenAccount\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"authority\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"hyperspace\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"hyperspaceTreasury\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"identifierPubkey\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"collectionBuyerTradeState\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"sellerTradeState\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"tokenProgram\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"systemProgram\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"ataProgram\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"metadataProgram\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"mplTokenAuthRulesProgram\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"programAsSigner\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"rent\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"instructions\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"ownerTokenRecord\";\n          isMut: true;\n          isSigner: false;\n          docs: [\"CHECK no check needed\"];\n        },\n        {\n          name: \"destinationTokenRecord\";\n          isMut: true;\n          isSigner: false;\n          docs: [\"CHECK no check needed\"];\n        },\n        {\n          name: \"authorizationRules\";\n          isMut: false;\n          isSigner: false;\n          docs: [\"CHECK no check needed\"];\n        }\n      ];\n      args: [\n        {\n          name: \"escrowPaymentBump\";\n          type: \"u8\";\n        },\n        {\n          name: \"programAsSignerBump\";\n          type: \"u8\";\n        },\n        {\n          name: \"buyerTradeStateBump\";\n          type: \"u8\";\n        },\n        {\n          name: \"sellerTradeStateBump\";\n          type: \"u8\";\n        },\n        {\n          name: \"maxAmountToPay\";\n          type: \"u64\";\n        },\n        {\n          name: \"buyerBrokerBasisPoints\";\n          type: \"u16\";\n        },\n        {\n          name: \"sellerBrokerBasisPoints\";\n          type: \"u16\";\n        },\n        {\n          name: \"tokenSize\";\n          type: \"u64\";\n        },\n        {\n          name: \"collectionTradeStateType\";\n          type: \"u8\";\n        },\n        {\n          name: \"identifierIndex\";\n          type: \"u8\";\n        },\n        {\n          name: \"royaltyBasisPoints\";\n          type: \"u16\";\n        }\n      ];\n    },\n    {\n      name: \"executeSale\";\n      accounts: [\n        {\n          name: \"buyer\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"buyerBrokerWallet\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"seller\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"sellerBrokerWallet\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"tokenAccount\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"tokenMint\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"metadata\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"escrowPaymentAccount\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"buyerReceiptTokenAccount\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"authority\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"hyperspace\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"hyperspaceTreasury\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"buyerTradeState\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"sellerTradeState\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"tokenProgram\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"systemProgram\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"ataProgram\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"metadataProgram\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"mplTokenAuthRulesProgram\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"programAsSigner\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"rent\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"instructions\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"editionAccount\";\n          isMut: false;\n          isSigner: false;\n          docs: [\"CHECK no check needed\"];\n        },\n        {\n          name: \"ownerTokenRecord\";\n          isMut: true;\n          isSigner: false;\n          docs: [\"CHECK no check needed\"];\n        },\n        {\n          name: \"destinationTokenRecord\";\n          isMut: true;\n          isSigner: false;\n          docs: [\"CHECK no check needed\"];\n        },\n        {\n          name: \"authorizationRules\";\n          isMut: false;\n          isSigner: false;\n          docs: [\"CHECK no check needed\"];\n        }\n      ];\n      args: [\n        {\n          name: \"escrowPaymentBump\";\n          type: \"u8\";\n        },\n        {\n          name: \"programAsSignerBump\";\n          type: \"u8\";\n        },\n        {\n          name: \"buyerTradeStateBump\";\n          type: \"u8\";\n        },\n        {\n          name: \"sellerTradeStateBump\";\n          type: \"u8\";\n        },\n        {\n          name: \"maxAmountToPay\";\n          type: \"u64\";\n        },\n        {\n          name: \"buyerBrokerBasisPoints\";\n          type: \"u16\";\n        },\n        {\n          name: \"sellerBrokerBasisPoints\";\n          type: \"u16\";\n        },\n        {\n          name: \"tokenSize\";\n          type: \"u64\";\n        },\n        {\n          name: \"royaltyBasisPoints\";\n          type: \"u16\";\n        }\n      ];\n    },\n    {\n      name: \"sell\";\n      accounts: [\n        {\n          name: \"wallet\";\n          isMut: false;\n          isSigner: true;\n        },\n        {\n          name: \"sellerBrokerWallet\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"tokenMint\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"tokenAccount\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"metadata\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"authority\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"hyperspace\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"hyperspaceFeeAccount\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"sellerTradeState\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"tokenProgram\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"systemProgram\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"metadataProgram\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"programAsSigner\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"instructions\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"tokenRecord\";\n          isMut: true;\n          isSigner: false;\n          docs: [\"CHECK no check needed\"];\n        },\n        {\n          name: \"editionAccount\";\n          isMut: false;\n          isSigner: false;\n          docs: [\"CHECK no check needed\"];\n        },\n        {\n          name: \"authorizationRules\";\n          isMut: false;\n          isSigner: false;\n          docs: [\"CHECK no check needed\"];\n        },\n        {\n          name: \"mplTokenAuthRulesProgram\";\n          isMut: false;\n          isSigner: false;\n          docs: [\"CHECK no check needed\"];\n        },\n        {\n          name: \"clock\";\n          isMut: false;\n          isSigner: false;\n        }\n      ];\n      args: [\n        {\n          name: \"tradeStateBump\";\n          type: \"u8\";\n        },\n        {\n          name: \"programAsSignerBump\";\n          type: \"u8\";\n        },\n        {\n          name: \"minAmountToReceive\";\n          type: \"u64\";\n        },\n        {\n          name: \"brokerBasisPoints\";\n          type: \"u16\";\n        },\n        {\n          name: \"tokenSize\";\n          type: \"u64\";\n        },\n        {\n          name: \"royaltyBasisPoints\";\n          type: \"u16\";\n        }\n      ];\n    },\n    {\n      name: \"buy\";\n      accounts: [\n        {\n          name: \"wallet\";\n          isMut: false;\n          isSigner: true;\n        },\n        {\n          name: \"buyerBrokerWallet\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"paymentAccount\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"transferAuthority\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"tokenAccount\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"buyerReceiptTokenAccount\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"metadata\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"escrowPaymentAccount\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"authority\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"hyperspace\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"hyperspaceFeeAccount\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"buyerTradeState\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"tokenProgram\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"systemProgram\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"rent\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"clock\";\n          isMut: false;\n          isSigner: false;\n        }\n      ];\n      args: [\n        {\n          name: \"tradeStateBump\";\n          type: \"u8\";\n        },\n        {\n          name: \"escrowPaymentBump\";\n          type: \"u8\";\n        },\n        {\n          name: \"maxAmountToPay\";\n          type: \"u64\";\n        },\n        {\n          name: \"brokerBasisPoints\";\n          type: \"u16\";\n        },\n        {\n          name: \"tokenSize\";\n          type: \"u64\";\n        },\n        {\n          name: \"royaltyBasisPoints\";\n          type: \"u16\";\n        }\n      ];\n    },\n    {\n      name: \"collectionBuy\";\n      accounts: [\n        {\n          name: \"wallet\";\n          isMut: true;\n          isSigner: true;\n        },\n        {\n          name: \"buyerBrokerWallet\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"identifierPubkey\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"escrowPaymentAccount\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"authority\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"hyperspace\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"hyperspaceFeeAccount\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"collectionBuyerTradeState\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"tokenProgram\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"systemProgram\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"rent\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"clock\";\n          isMut: false;\n          isSigner: false;\n        }\n      ];\n      args: [\n        {\n          name: \"tradeStateBump\";\n          type: \"u8\";\n        },\n        {\n          name: \"escrowPaymentBump\";\n          type: \"u8\";\n        },\n        {\n          name: \"buyerPrice\";\n          type: \"u64\";\n        },\n        {\n          name: \"brokerBasisPoints\";\n          type: \"u16\";\n        },\n        {\n          name: \"collectionTradeStateType\";\n          type: \"u8\";\n        },\n        {\n          name: \"identifierIndex\";\n          type: \"u8\";\n        },\n        {\n          name: \"royaltyBasisPoints\";\n          type: \"u16\";\n        }\n      ];\n    },\n    {\n      name: \"createCollectionBuyTradeState\";\n      accounts: [\n        {\n          name: \"wallet\";\n          isMut: true;\n          isSigner: true;\n        },\n        {\n          name: \"identifierPubkey\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"brokerWallet\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"tradeState\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"systemProgram\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"clock\";\n          isMut: false;\n          isSigner: false;\n        }\n      ];\n      args: [\n        {\n          name: \"tradeStateBump\";\n          type: \"u8\";\n        },\n        {\n          name: \"buyPrice\";\n          type: \"u64\";\n        },\n        {\n          name: \"brokerBasisPoints\";\n          type: \"u16\";\n        },\n        {\n          name: \"collectionTradeStateType\";\n          type: \"u8\";\n        },\n        {\n          name: \"identifierIndex\";\n          type: \"u8\";\n        },\n        {\n          name: \"royaltyBasisPoints\";\n          type: \"u16\";\n        }\n      ];\n    },\n    {\n      name: \"createTradeState\";\n      accounts: [\n        {\n          name: \"wallet\";\n          isMut: true;\n          isSigner: true;\n        },\n        {\n          name: \"collection\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"brokerWallet\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"tokenAccount\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"tokenMint\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"tradeState\";\n          isMut: true;\n          isSigner: false;\n        },\n        {\n          name: \"systemProgram\";\n          isMut: false;\n          isSigner: false;\n        },\n        {\n          name: \"clock\";\n          isMut: false;\n          isSigner: false;\n        }\n      ];\n      args: [\n        {\n          name: \"isBuy\";\n          type: \"u8\";\n        },\n        {\n          name: \"tradeStateBump\";\n          type: \"u8\";\n        },\n        {\n          name: \"buyPrice\";\n          type: \"u64\";\n        },\n        {\n          name: \"brokerBasisPoints\";\n          type: \"u16\";\n        },\n        {\n          name: \"tokenSize\";\n          type: \"u64\";\n        },\n        {\n          name: \"royaltyBasisPoints\";\n          type: \"u16\";\n        }\n      ];\n    }\n  ];\n  accounts: [\n    {\n      name: \"hyperspace\";\n      type: {\n        kind: \"struct\";\n        fields: [\n          {\n            name: \"hyperspaceFeeAccount\";\n            type: \"publicKey\";\n          },\n          {\n            name: \"hyperspaceTreasury\";\n            type: \"publicKey\";\n          },\n          {\n            name: \"treasuryWithdrawalDestination\";\n            type: \"publicKey\";\n          },\n          {\n            name: \"feeWithdrawalDestination\";\n            type: \"publicKey\";\n          },\n          {\n            name: \"authority\";\n            type: \"publicKey\";\n          },\n          {\n            name: \"creator\";\n            type: \"publicKey\";\n          },\n          {\n            name: \"bump\";\n            type: \"u8\";\n          },\n          {\n            name: \"treasuryBump\";\n            type: \"u8\";\n          },\n          {\n            name: \"feePayerBump\";\n            type: \"u8\";\n          },\n          {\n            name: \"sellerFeeBasisPoints\";\n            type: \"u16\";\n          },\n          {\n            name: \"requiresSignOff\";\n            type: \"bool\";\n          },\n          {\n            name: \"canChangeSalePrice\";\n            type: \"bool\";\n          }\n        ];\n      };\n    },\n    {\n      name: \"programAsSigner\";\n      type: {\n        kind: \"struct\";\n        fields: [\n          {\n            name: \"firstByte\";\n            type: \"u8\";\n          }\n        ];\n      };\n    },\n    {\n      name: \"tradeState\";\n      type: {\n        kind: \"struct\";\n        fields: [\n          {\n            name: \"buyPrice\";\n            type: \"u64\";\n          },\n          {\n            name: \"userWallet\";\n            type: \"publicKey\";\n          },\n          {\n            name: \"isBuy\";\n            type: \"u8\";\n          },\n          {\n            name: \"collection\";\n            type: \"publicKey\";\n          },\n          {\n            name: \"brokerWallet\";\n            type: \"publicKey\";\n          },\n          {\n            name: \"brokerBasisPoints\";\n            type: \"u16\";\n          },\n          {\n            name: \"tokenMint\";\n            type: \"publicKey\";\n          },\n          {\n            name: \"royaltyBasisPoints\";\n            type: \"u16\";\n          },\n          {\n            name: \"timestamp\";\n            type: \"i64\";\n          }\n        ];\n      };\n    },\n    {\n      name: \"collectionBuyTradeState\";\n      type: {\n        kind: \"struct\";\n        fields: [\n          {\n            name: \"userWallet\";\n            type: \"publicKey\";\n          },\n          {\n            name: \"collectionTradeStateType\";\n            type: \"u8\";\n          },\n          {\n            name: \"identifierIndex\";\n            type: \"u8\";\n          },\n          {\n            name: \"identifierPubkey\";\n            type: \"publicKey\";\n          },\n          {\n            name: \"brokerWallet\";\n            type: \"publicKey\";\n          },\n          {\n            name: \"brokerBasisPoints\";\n            type: \"u16\";\n          },\n          {\n            name: \"royaltyBasisPoints\";\n            type: \"u16\";\n          },\n          {\n            name: \"timestamp\";\n            type: \"i64\";\n          }\n        ];\n      };\n    }\n  ];\n  types: [\n    {\n      name: \"AuthorizationDataLocal\";\n      type: {\n        kind: \"struct\";\n        fields: [\n          {\n            name: \"payload\";\n            type: {\n              vec: {\n                defined: \"TaggedPayload\";\n              };\n            };\n          }\n        ];\n      };\n    },\n    {\n      name: \"TaggedPayload\";\n      type: {\n        kind: \"struct\";\n        fields: [\n          {\n            name: \"name\";\n            type: \"string\";\n          },\n          {\n            name: \"payload\";\n            type: {\n              defined: \"PayloadTypeLocal\";\n            };\n          }\n        ];\n      };\n    },\n    {\n      name: \"SeedsVecLocal\";\n      type: {\n        kind: \"struct\";\n        fields: [\n          {\n            name: \"seeds\";\n            docs: [\"The vector of derivation seeds.\"];\n            type: {\n              vec: \"bytes\";\n            };\n          }\n        ];\n      };\n    },\n    {\n      name: \"ProofInfoLocal\";\n      type: {\n        kind: \"struct\";\n        fields: [\n          {\n            name: \"proof\";\n            docs: [\"The merkle proof.\"];\n            type: {\n              vec: {\n                array: [\"u8\", 32];\n              };\n            };\n          }\n        ];\n      };\n    },\n    {\n      name: \"PayloadTypeLocal\";\n      type: {\n        kind: \"enum\";\n        variants: [\n          {\n            name: \"Pubkey\";\n            fields: [\"publicKey\"];\n          },\n          {\n            name: \"Seeds\";\n            fields: [\n              {\n                defined: \"SeedsVecLocal\";\n              }\n            ];\n          },\n          {\n            name: \"MerkleProof\";\n            fields: [\n              {\n                defined: \"ProofInfoLocal\";\n              }\n            ];\n          },\n          {\n            name: \"Number\";\n            fields: [\"u64\"];\n          }\n        ];\n      };\n    }\n  ];\n  errors: [\n    {\n      code: 6000;\n      name: \"PublicKeyMismatch\";\n      msg: \"PublicKeyMismatch\";\n    },\n    {\n      code: 6001;\n      name: \"InvalidMintAuthority\";\n      msg: \"InvalidMintAuthority\";\n    },\n    {\n      code: 6002;\n      name: \"UninitializedAccount\";\n      msg: \"UninitializedAccount\";\n    },\n    {\n      code: 6003;\n      name: \"IncorrectOwner\";\n      msg: \"IncorrectOwner\";\n    },\n    {\n      code: 6004;\n      name: \"PublicKeysShouldBeUnique\";\n      msg: \"PublicKeysShouldBeUnique\";\n    },\n    {\n      code: 6005;\n      name: \"StatementFalse\";\n      msg: \"StatementFalse\";\n    },\n    {\n      code: 6006;\n      name: \"NotRentExempt\";\n      msg: \"NotRentExempt\";\n    },\n    {\n      code: 6007;\n      name: \"NumericalOverflow\";\n      msg: \"NumericalOverflow\";\n    },\n    {\n      code: 6008;\n      name: \"ExpectedSolAccount\";\n      msg: \"Expected a sol account but got an spl token account instead\";\n    },\n    {\n      code: 6009;\n      name: \"CannotExchangeSOLForSol\";\n      msg: \"Cannot exchange sol for sol\";\n    },\n    {\n      code: 6010;\n      name: \"SOLWalletMustSign\";\n      msg: \"If paying with sol, sol wallet must be signer\";\n    },\n    {\n      code: 6011;\n      name: \"CannotTakeThisActionWithoutHyperspaceSignOff\";\n      msg: \"Cannot take this action without hyperspace signing too\";\n    },\n    {\n      code: 6012;\n      name: \"NoPayerPresent\";\n      msg: \"No payer present on this txn\";\n    },\n    {\n      code: 6013;\n      name: \"DerivedKeyInvalid\";\n      msg: \"Derived key invalid\";\n    },\n    {\n      code: 6014;\n      name: \"MetadataDoesntExist\";\n      msg: \"Metadata doesn't exist\";\n    },\n    {\n      code: 6015;\n      name: \"InvalidTokenAmount\";\n      msg: \"Invalid token amount\";\n    },\n    {\n      code: 6016;\n      name: \"BothPartiesNeedToAgreeToSale\";\n      msg: \"Both parties need to agree to this sale\";\n    },\n    {\n      code: 6017;\n      name: \"CannotMatchFreeSalesWithoutHyperspaceOrSellerSignoff\";\n      msg: \"Cannot match free sales unless the hyperspace or seller signs off\";\n    },\n    {\n      code: 6018;\n      name: \"SaleRequiresSigner\";\n      msg: \"This sale requires a signer\";\n    },\n    {\n      code: 6019;\n      name: \"OldSellerNotInitialized\";\n      msg: \"Old seller not initialized\";\n    },\n    {\n      code: 6020;\n      name: \"SellerATACannotHaveDelegate\";\n      msg: \"Seller ata cannot have a delegate set\";\n    },\n    {\n      code: 6021;\n      name: \"BuyerATACannotHaveDelegate\";\n      msg: \"Buyer ata cannot have a delegate set\";\n    },\n    {\n      code: 6022;\n      name: \"NoValidSignerPresent\";\n      msg: \"No valid signer present\";\n    },\n    {\n      code: 6023;\n      name: \"InvalidBasisPoints\";\n      msg: \"BP must be less than or equal to 10000\";\n    },\n    {\n      code: 6024;\n      name: \"InvalidBrokerInformation\";\n      msg: \"Broker information must match\";\n    },\n    {\n      code: 6025;\n      name: \"InvalidTokenAccount\";\n      msg: \"Token Account holding selling token must be owned by seller\";\n    },\n    {\n      code: 6026;\n      name: \"InvalidPermissionlessCancel\";\n      msg: \"Cannot permissionless cancel this trade state\";\n    },\n    {\n      code: 6027;\n      name: \"InvalidCollectionTradeStateType\";\n      msg: \"Invalid Collection Trade State Type\";\n    },\n    {\n      code: 6028;\n      name: \"InvalidCollectionTradeStateIdentifier\";\n      msg: \"Invalid Collection Trade State Identifier\";\n    },\n    {\n      code: 6029;\n      name: \"BumpSeedNotInHashMap\";\n      msg: \"Bump seed not in hash map.\";\n    },\n    {\n      code: 6030;\n      name: \"MetaplexTransferFailed\";\n      msg: \"Failed to transfer NFT using metaplex\";\n    },\n    {\n      code: 6031;\n      name: \"MetaplexDelegateFailed\";\n      msg: \"Failed to set Sale delegate on NFT using metaplex\";\n    },\n    {\n      code: 6032;\n      name: \"MetaplexRevokeFailed\";\n      msg: \"Failed to revoke Sale delegate on NFT using metaplex\";\n    },\n    {\n      code: 6033;\n      name: \"IncorrectTokenStandard\";\n      msg: \"Token standard must be Programmable NFT or Standard NFT\";\n    },\n    {\n      code: 6034;\n      name: \"PNFTDelegateSetAlready\";\n      msg: \"PNFT Delegate set already\";\n    },\n    {\n      code: 6035;\n      name: \"RoyaltyBPMismatch\";\n      msg: \"Royalty basis points mismatch\";\n    }\n  ];\n};\n\nexport const hyperspaceIdl: Hyperspace = {\n  version: \"0.1.0\",\n  name: \"hyperspace\",\n  instructions: [\n    {\n      name: \"initProgramAsSigner\",\n      accounts: [\n        {\n          name: \"wallet\",\n          isMut: true,\n          isSigner: true,\n        },\n        {\n          name: \"programAsSigner\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"systemProgram\",\n          isMut: false,\n          isSigner: false,\n        },\n      ],\n      args: [\n        {\n          name: \"programAsSignerBump\",\n          type: \"u8\",\n        },\n      ],\n    },\n    {\n      name: \"updateHyperspace\",\n      accounts: [\n        {\n          name: \"payer\",\n          isMut: false,\n          isSigner: true,\n        },\n        {\n          name: \"authority\",\n          isMut: false,\n          isSigner: true,\n        },\n        {\n          name: \"newAuthority\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"feeWithdrawalDestination\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"treasuryWithdrawalDestination\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"treasuryWithdrawalDestinationOwner\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"hyperspace\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"tokenProgram\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"systemProgram\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"ataProgram\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"rent\",\n          isMut: false,\n          isSigner: false,\n        },\n      ],\n      args: [\n        {\n          name: \"sellerFeeBasisPoints\",\n          type: {\n            option: \"u16\",\n          },\n        },\n        {\n          name: \"requiresSignOff\",\n          type: {\n            option: \"bool\",\n          },\n        },\n        {\n          name: \"canChangeSalePrice\",\n          type: {\n            option: \"bool\",\n          },\n        },\n      ],\n    },\n    {\n      name: \"withdraw\",\n      accounts: [\n        {\n          name: \"wallet\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"receiptAccount\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"escrowPaymentAccount\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"authority\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"hyperspace\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"tokenProgram\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"systemProgram\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"rent\",\n          isMut: false,\n          isSigner: false,\n        },\n      ],\n      args: [\n        {\n          name: \"escrowPaymentBump\",\n          type: \"u8\",\n        },\n        {\n          name: \"amount\",\n          type: \"u64\",\n        },\n      ],\n    },\n    {\n      name: \"deposit\",\n      accounts: [\n        {\n          name: \"wallet\",\n          isMut: false,\n          isSigner: true,\n        },\n        {\n          name: \"paymentAccount\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"transferAuthority\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"escrowPaymentAccount\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"authority\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"hyperspace\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"hyperspaceFeeAccount\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"tokenProgram\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"systemProgram\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"rent\",\n          isMut: false,\n          isSigner: false,\n        },\n      ],\n      args: [\n        {\n          name: \"escrowPaymentBump\",\n          type: \"u8\",\n        },\n        {\n          name: \"amount\",\n          type: \"u64\",\n        },\n      ],\n    },\n    {\n      name: \"permissionlessCancel\",\n      accounts: [\n        {\n          name: \"wallet\",\n          isMut: false,\n          isSigner: true,\n        },\n        {\n          name: \"tokenMint\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"seller\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"tokenAccount\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"tradeState\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"programAsSigner\",\n          isMut: false,\n          isSigner: false,\n        },\n      ],\n      args: [\n        {\n          name: \"programAsSignerBump\",\n          type: \"u8\",\n        },\n        {\n          name: \"tradeStateBump\",\n          type: \"u8\",\n        },\n        {\n          name: \"tokenSize\",\n          type: \"u64\",\n        },\n      ],\n    },\n    {\n      name: \"collectionCancelBuy\",\n      accounts: [\n        {\n          name: \"wallet\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"identifierPubkey\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"authority\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"hyperspace\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"collectionBuyerTradeState\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"programAsSigner\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"tokenProgram\",\n          isMut: false,\n          isSigner: false,\n        },\n      ],\n      args: [\n        {\n          name: \"buyerPrice\",\n          type: \"u64\",\n        },\n        {\n          name: \"programAsSignerBump\",\n          type: \"u8\",\n        },\n        {\n          name: \"tradeStateBump\",\n          type: \"u8\",\n        },\n        {\n          name: \"collectionTradeStateType\",\n          type: \"u8\",\n        },\n        {\n          name: \"identifierIndex\",\n          type: \"u8\",\n        },\n      ],\n    },\n    {\n      name: \"cancel\",\n      accounts: [\n        {\n          name: \"wallet\",\n          isMut: true,\n          isSigner: false,\n          docs: [\"CHECK OWNER OF TRADE_STATE TODO\"],\n        },\n        {\n          name: \"tokenMint\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"tokenAccount\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"metadata\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"authority\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"hyperspace\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"tradeState\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"tokenProgram\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"systemProgram\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"metadataProgram\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"programAsSigner\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"instructions\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"tokenRecord\",\n          isMut: true,\n          isSigner: false,\n          docs: [\"CHECK no check needed\"],\n        },\n        {\n          name: \"editionAccount\",\n          isMut: false,\n          isSigner: false,\n          docs: [\"CHECK no check needed\"],\n        },\n        {\n          name: \"authorizationRules\",\n          isMut: false,\n          isSigner: false,\n          docs: [\"CHECK no check needed\"],\n        },\n        {\n          name: \"mplTokenAuthRulesProgram\",\n          isMut: false,\n          isSigner: false,\n          docs: [\"CHECK no check needed\"],\n        },\n      ],\n      args: [\n        {\n          name: \"isBuy\",\n          type: \"u8\",\n        },\n        {\n          name: \"programAsSignerBump\",\n          type: \"u8\",\n        },\n        {\n          name: \"tradeStateBump\",\n          type: \"u8\",\n        },\n        {\n          name: \"tokenSize\",\n          type: \"u64\",\n        },\n      ],\n    },\n    {\n      name: \"collectionExecuteSale\",\n      accounts: [\n        {\n          name: \"buyer\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"buyerBrokerWallet\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"seller\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"sellerBrokerWallet\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"tokenAccount\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"tokenMint\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"metadata\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"editionData\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"escrowPaymentAccount\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"buyerReceiptTokenAccount\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"authority\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"hyperspace\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"hyperspaceTreasury\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"identifierPubkey\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"collectionBuyerTradeState\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"sellerTradeState\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"tokenProgram\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"systemProgram\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"ataProgram\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"metadataProgram\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"mplTokenAuthRulesProgram\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"programAsSigner\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"rent\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"instructions\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"ownerTokenRecord\",\n          isMut: true,\n          isSigner: false,\n          docs: [\"CHECK no check needed\"],\n        },\n        {\n          name: \"destinationTokenRecord\",\n          isMut: true,\n          isSigner: false,\n          docs: [\"CHECK no check needed\"],\n        },\n        {\n          name: \"authorizationRules\",\n          isMut: false,\n          isSigner: false,\n          docs: [\"CHECK no check needed\"],\n        },\n      ],\n      args: [\n        {\n          name: \"escrowPaymentBump\",\n          type: \"u8\",\n        },\n        {\n          name: \"programAsSignerBump\",\n          type: \"u8\",\n        },\n        {\n          name: \"buyerTradeStateBump\",\n          type: \"u8\",\n        },\n        {\n          name: \"sellerTradeStateBump\",\n          type: \"u8\",\n        },\n        {\n          name: \"maxAmountToPay\",\n          type: \"u64\",\n        },\n        {\n          name: \"buyerBrokerBasisPoints\",\n          type: \"u16\",\n        },\n        {\n          name: \"sellerBrokerBasisPoints\",\n          type: \"u16\",\n        },\n        {\n          name: \"tokenSize\",\n          type: \"u64\",\n        },\n        {\n          name: \"collectionTradeStateType\",\n          type: \"u8\",\n        },\n        {\n          name: \"identifierIndex\",\n          type: \"u8\",\n        },\n        {\n          name: \"royaltyBasisPoints\",\n          type: \"u16\",\n        },\n      ],\n    },\n    {\n      name: \"executeSale\",\n      accounts: [\n        {\n          name: \"buyer\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"buyerBrokerWallet\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"seller\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"sellerBrokerWallet\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"tokenAccount\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"tokenMint\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"metadata\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"escrowPaymentAccount\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"buyerReceiptTokenAccount\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"authority\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"hyperspace\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"hyperspaceTreasury\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"buyerTradeState\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"sellerTradeState\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"tokenProgram\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"systemProgram\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"ataProgram\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"metadataProgram\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"mplTokenAuthRulesProgram\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"programAsSigner\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"rent\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"instructions\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"editionAccount\",\n          isMut: false,\n          isSigner: false,\n          docs: [\"CHECK no check needed\"],\n        },\n        {\n          name: \"ownerTokenRecord\",\n          isMut: true,\n          isSigner: false,\n          docs: [\"CHECK no check needed\"],\n        },\n        {\n          name: \"destinationTokenRecord\",\n          isMut: true,\n          isSigner: false,\n          docs: [\"CHECK no check needed\"],\n        },\n        {\n          name: \"authorizationRules\",\n          isMut: false,\n          isSigner: false,\n          docs: [\"CHECK no check needed\"],\n        },\n      ],\n      args: [\n        {\n          name: \"escrowPaymentBump\",\n          type: \"u8\",\n        },\n        {\n          name: \"programAsSignerBump\",\n          type: \"u8\",\n        },\n        {\n          name: \"buyerTradeStateBump\",\n          type: \"u8\",\n        },\n        {\n          name: \"sellerTradeStateBump\",\n          type: \"u8\",\n        },\n        {\n          name: \"maxAmountToPay\",\n          type: \"u64\",\n        },\n        {\n          name: \"buyerBrokerBasisPoints\",\n          type: \"u16\",\n        },\n        {\n          name: \"sellerBrokerBasisPoints\",\n          type: \"u16\",\n        },\n        {\n          name: \"tokenSize\",\n          type: \"u64\",\n        },\n        {\n          name: \"royaltyBasisPoints\",\n          type: \"u16\",\n        },\n      ],\n    },\n    {\n      name: \"sell\",\n      accounts: [\n        {\n          name: \"wallet\",\n          isMut: false,\n          isSigner: true,\n        },\n        {\n          name: \"sellerBrokerWallet\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"tokenMint\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"tokenAccount\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"metadata\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"authority\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"hyperspace\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"hyperspaceFeeAccount\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"sellerTradeState\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"tokenProgram\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"systemProgram\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"metadataProgram\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"programAsSigner\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"instructions\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"tokenRecord\",\n          isMut: true,\n          isSigner: false,\n          docs: [\"CHECK no check needed\"],\n        },\n        {\n          name: \"editionAccount\",\n          isMut: false,\n          isSigner: false,\n          docs: [\"CHECK no check needed\"],\n        },\n        {\n          name: \"authorizationRules\",\n          isMut: false,\n          isSigner: false,\n          docs: [\"CHECK no check needed\"],\n        },\n        {\n          name: \"mplTokenAuthRulesProgram\",\n          isMut: false,\n          isSigner: false,\n          docs: [\"CHECK no check needed\"],\n        },\n        {\n          name: \"clock\",\n          isMut: false,\n          isSigner: false,\n        },\n      ],\n      args: [\n        {\n          name: \"tradeStateBump\",\n          type: \"u8\",\n        },\n        {\n          name: \"programAsSignerBump\",\n          type: \"u8\",\n        },\n        {\n          name: \"minAmountToReceive\",\n          type: \"u64\",\n        },\n        {\n          name: \"brokerBasisPoints\",\n          type: \"u16\",\n        },\n        {\n          name: \"tokenSize\",\n          type: \"u64\",\n        },\n        {\n          name: \"royaltyBasisPoints\",\n          type: \"u16\",\n        },\n      ],\n    },\n    {\n      name: \"buy\",\n      accounts: [\n        {\n          name: \"wallet\",\n          isMut: false,\n          isSigner: true,\n        },\n        {\n          name: \"buyerBrokerWallet\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"paymentAccount\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"transferAuthority\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"tokenAccount\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"buyerReceiptTokenAccount\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"metadata\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"escrowPaymentAccount\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"authority\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"hyperspace\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"hyperspaceFeeAccount\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"buyerTradeState\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"tokenProgram\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"systemProgram\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"rent\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"clock\",\n          isMut: false,\n          isSigner: false,\n        },\n      ],\n      args: [\n        {\n          name: \"tradeStateBump\",\n          type: \"u8\",\n        },\n        {\n          name: \"escrowPaymentBump\",\n          type: \"u8\",\n        },\n        {\n          name: \"maxAmountToPay\",\n          type: \"u64\",\n        },\n        {\n          name: \"brokerBasisPoints\",\n          type: \"u16\",\n        },\n        {\n          name: \"tokenSize\",\n          type: \"u64\",\n        },\n        {\n          name: \"royaltyBasisPoints\",\n          type: \"u16\",\n        },\n      ],\n    },\n    {\n      name: \"collectionBuy\",\n      accounts: [\n        {\n          name: \"wallet\",\n          isMut: true,\n          isSigner: true,\n        },\n        {\n          name: \"buyerBrokerWallet\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"identifierPubkey\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"escrowPaymentAccount\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"authority\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"hyperspace\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"hyperspaceFeeAccount\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"collectionBuyerTradeState\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"tokenProgram\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"systemProgram\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"rent\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"clock\",\n          isMut: false,\n          isSigner: false,\n        },\n      ],\n      args: [\n        {\n          name: \"tradeStateBump\",\n          type: \"u8\",\n        },\n        {\n          name: \"escrowPaymentBump\",\n          type: \"u8\",\n        },\n        {\n          name: \"buyerPrice\",\n          type: \"u64\",\n        },\n        {\n          name: \"brokerBasisPoints\",\n          type: \"u16\",\n        },\n        {\n          name: \"collectionTradeStateType\",\n          type: \"u8\",\n        },\n        {\n          name: \"identifierIndex\",\n          type: \"u8\",\n        },\n        {\n          name: \"royaltyBasisPoints\",\n          type: \"u16\",\n        },\n      ],\n    },\n    {\n      name: \"createCollectionBuyTradeState\",\n      accounts: [\n        {\n          name: \"wallet\",\n          isMut: true,\n          isSigner: true,\n        },\n        {\n          name: \"identifierPubkey\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"brokerWallet\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"tradeState\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"systemProgram\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"clock\",\n          isMut: false,\n          isSigner: false,\n        },\n      ],\n      args: [\n        {\n          name: \"tradeStateBump\",\n          type: \"u8\",\n        },\n        {\n          name: \"buyPrice\",\n          type: \"u64\",\n        },\n        {\n          name: \"brokerBasisPoints\",\n          type: \"u16\",\n        },\n        {\n          name: \"collectionTradeStateType\",\n          type: \"u8\",\n        },\n        {\n          name: \"identifierIndex\",\n          type: \"u8\",\n        },\n        {\n          name: \"royaltyBasisPoints\",\n          type: \"u16\",\n        },\n      ],\n    },\n    {\n      name: \"createTradeState\",\n      accounts: [\n        {\n          name: \"wallet\",\n          isMut: true,\n          isSigner: true,\n        },\n        {\n          name: \"collection\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"brokerWallet\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"tokenAccount\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"tokenMint\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"tradeState\",\n          isMut: true,\n          isSigner: false,\n        },\n        {\n          name: \"systemProgram\",\n          isMut: false,\n          isSigner: false,\n        },\n        {\n          name: \"clock\",\n          isMut: false,\n          isSigner: false,\n        },\n      ],\n      args: [\n        {\n          name: \"isBuy\",\n          type: \"u8\",\n        },\n        {\n          name: \"tradeStateBump\",\n          type: \"u8\",\n        },\n        {\n          name: \"buyPrice\",\n          type: \"u64\",\n        },\n        {\n          name: \"brokerBasisPoints\",\n          type: \"u16\",\n        },\n        {\n          name: \"tokenSize\",\n          type: \"u64\",\n        },\n        {\n          name: \"royaltyBasisPoints\",\n          type: \"u16\",\n        },\n      ],\n    },\n  ],\n  accounts: [\n    {\n      name: \"hyperspace\",\n      type: {\n        kind: \"struct\",\n        fields: [\n          {\n            name: \"hyperspaceFeeAccount\",\n            type: \"publicKey\",\n          },\n          {\n            name: \"hyperspaceTreasury\",\n            type: \"publicKey\",\n          },\n          {\n            name: \"treasuryWithdrawalDestination\",\n            type: \"publicKey\",\n          },\n          {\n            name: \"feeWithdrawalDestination\",\n            type: \"publicKey\",\n          },\n          {\n            name: \"authority\",\n            type: \"publicKey\",\n          },\n          {\n            name: \"creator\",\n            type: \"publicKey\",\n          },\n          {\n            name: \"bump\",\n            type: \"u8\",\n          },\n          {\n            name: \"treasuryBump\",\n            type: \"u8\",\n          },\n          {\n            name: \"feePayerBump\",\n            type: \"u8\",\n          },\n          {\n            name: \"sellerFeeBasisPoints\",\n            type: \"u16\",\n          },\n          {\n            name: \"requiresSignOff\",\n            type: \"bool\",\n          },\n          {\n            name: \"canChangeSalePrice\",\n            type: \"bool\",\n          },\n        ],\n      },\n    },\n    {\n      name: \"programAsSigner\",\n      type: {\n        kind: \"struct\",\n        fields: [\n          {\n            name: \"firstByte\",\n            type: \"u8\",\n          },\n        ],\n      },\n    },\n    {\n      name: \"tradeState\",\n      type: {\n        kind: \"struct\",\n        fields: [\n          {\n            name: \"buyPrice\",\n            type: \"u64\",\n          },\n          {\n            name: \"userWallet\",\n            type: \"publicKey\",\n          },\n          {\n            name: \"isBuy\",\n            type: \"u8\",\n          },\n          {\n            name: \"collection\",\n            type: \"publicKey\",\n          },\n          {\n            name: \"brokerWallet\",\n            type: \"publicKey\",\n          },\n          {\n            name: \"brokerBasisPoints\",\n            type: \"u16\",\n          },\n          {\n            name: \"tokenMint\",\n            type: \"publicKey\",\n          },\n          {\n            name: \"royaltyBasisPoints\",\n            type: \"u16\",\n          },\n          {\n            name: \"timestamp\",\n            type: \"i64\",\n          },\n        ],\n      },\n    },\n    {\n      name: \"collectionBuyTradeState\",\n      type: {\n        kind: \"struct\",\n        fields: [\n          {\n            name: \"userWallet\",\n            type: \"publicKey\",\n          },\n          {\n            name: \"collectionTradeStateType\",\n            type: \"u8\",\n          },\n          {\n            name: \"identifierIndex\",\n            type: \"u8\",\n          },\n          {\n            name: \"identifierPubkey\",\n            type: \"publicKey\",\n          },\n          {\n            name: \"brokerWallet\",\n            type: \"publicKey\",\n          },\n          {\n            name: \"brokerBasisPoints\",\n            type: \"u16\",\n          },\n          {\n            name: \"royaltyBasisPoints\",\n            type: \"u16\",\n          },\n          {\n            name: \"timestamp\",\n            type: \"i64\",\n          },\n        ],\n      },\n    },\n  ],\n  types: [\n    {\n      name: \"AuthorizationDataLocal\",\n      type: {\n        kind: \"struct\",\n        fields: [\n          {\n            name: \"payload\",\n            type: {\n              vec: {\n                defined: \"TaggedPayload\",\n              },\n            },\n          },\n        ],\n      },\n    },\n    {\n      name: \"TaggedPayload\",\n      type: {\n        kind: \"struct\",\n        fields: [\n          {\n            name: \"name\",\n            type: \"string\",\n          },\n          {\n            name: \"payload\",\n            type: {\n              defined: \"PayloadTypeLocal\",\n            },\n          },\n        ],\n      },\n    },\n    {\n      name: \"SeedsVecLocal\",\n      type: {\n        kind: \"struct\",\n        fields: [\n          {\n            name: \"seeds\",\n            docs: [\"The vector of derivation seeds.\"],\n            type: {\n              vec: \"bytes\",\n            },\n          },\n        ],\n      },\n    },\n    {\n      name: \"ProofInfoLocal\",\n      type: {\n        kind: \"struct\",\n        fields: [\n          {\n            name: \"proof\",\n            docs: [\"The merkle proof.\"],\n            type: {\n              vec: {\n                array: [\"u8\", 32],\n              },\n            },\n          },\n        ],\n      },\n    },\n    {\n      name: \"PayloadTypeLocal\",\n      type: {\n        kind: \"enum\",\n        variants: [\n          {\n            name: \"Pubkey\",\n            fields: [\"publicKey\"],\n          },\n          {\n            name: \"Seeds\",\n            fields: [\n              {\n                defined: \"SeedsVecLocal\",\n              },\n            ],\n          },\n          {\n            name: \"MerkleProof\",\n            fields: [\n              {\n                defined: \"ProofInfoLocal\",\n              },\n            ],\n          },\n          {\n            name: \"Number\",\n            fields: [\"u64\"],\n          },\n        ],\n      },\n    },\n  ],\n  errors: [\n    {\n      code: 6000,\n      name: \"PublicKeyMismatch\",\n      msg: \"PublicKeyMismatch\",\n    },\n    {\n      code: 6001,\n      name: \"InvalidMintAuthority\",\n      msg: \"InvalidMintAuthority\",\n    },\n    {\n      code: 6002,\n      name: \"UninitializedAccount\",\n      msg: \"UninitializedAccount\",\n    },\n    {\n      code: 6003,\n      name: \"IncorrectOwner\",\n      msg: \"IncorrectOwner\",\n    },\n    {\n      code: 6004,\n      name: \"PublicKeysShouldBeUnique\",\n      msg: \"PublicKeysShouldBeUnique\",\n    },\n    {\n      code: 6005,\n      name: \"StatementFalse\",\n      msg: \"StatementFalse\",\n    },\n    {\n      code: 6006,\n      name: \"NotRentExempt\",\n      msg: \"NotRentExempt\",\n    },\n    {\n      code: 6007,\n      name: \"NumericalOverflow\",\n      msg: \"NumericalOverflow\",\n    },\n    {\n      code: 6008,\n      name: \"ExpectedSolAccount\",\n      msg: \"Expected a sol account but got an spl token account instead\",\n    },\n    {\n      code: 6009,\n      name: \"CannotExchangeSOLForSol\",\n      msg: \"Cannot exchange sol for sol\",\n    },\n    {\n      code: 6010,\n      name: \"SOLWalletMustSign\",\n      msg: \"If paying with sol, sol wallet must be signer\",\n    },\n    {\n      code: 6011,\n      name: \"CannotTakeThisActionWithoutHyperspaceSignOff\",\n      msg: \"Cannot take this action without hyperspace signing too\",\n    },\n    {\n      code: 6012,\n      name: \"NoPayerPresent\",\n      msg: \"No payer present on this txn\",\n    },\n    {\n      code: 6013,\n      name: \"DerivedKeyInvalid\",\n      msg: \"Derived key invalid\",\n    },\n    {\n      code: 6014,\n      name: \"MetadataDoesntExist\",\n      msg: \"Metadata doesn't exist\",\n    },\n    {\n      code: 6015,\n      name: \"InvalidTokenAmount\",\n      msg: \"Invalid token amount\",\n    },\n    {\n      code: 6016,\n      name: \"BothPartiesNeedToAgreeToSale\",\n      msg: \"Both parties need to agree to this sale\",\n    },\n    {\n      code: 6017,\n      name: \"CannotMatchFreeSalesWithoutHyperspaceOrSellerSignoff\",\n      msg: \"Cannot match free sales unless the hyperspace or seller signs off\",\n    },\n    {\n      code: 6018,\n      name: \"SaleRequiresSigner\",\n      msg: \"This sale requires a signer\",\n    },\n    {\n      code: 6019,\n      name: \"OldSellerNotInitialized\",\n      msg: \"Old seller not initialized\",\n    },\n    {\n      code: 6020,\n      name: \"SellerATACannotHaveDelegate\",\n      msg: \"Seller ata cannot have a delegate set\",\n    },\n    {\n      code: 6021,\n      name: \"BuyerATACannotHaveDelegate\",\n      msg: \"Buyer ata cannot have a delegate set\",\n    },\n    {\n      code: 6022,\n      name: \"NoValidSignerPresent\",\n      msg: \"No valid signer present\",\n    },\n    {\n      code: 6023,\n      name: \"InvalidBasisPoints\",\n      msg: \"BP must be less than or equal to 10000\",\n    },\n    {\n      code: 6024,\n      name: \"InvalidBrokerInformation\",\n      msg: \"Broker information must match\",\n    },\n    {\n      code: 6025,\n      name: \"InvalidTokenAccount\",\n      msg: \"Token Account holding selling token must be owned by seller\",\n    },\n    {\n      code: 6026,\n      name: \"InvalidPermissionlessCancel\",\n      msg: \"Cannot permissionless cancel this trade state\",\n    },\n    {\n      code: 6027,\n      name: \"InvalidCollectionTradeStateType\",\n      msg: \"Invalid Collection Trade State Type\",\n    },\n    {\n      code: 6028,\n      name: \"InvalidCollectionTradeStateIdentifier\",\n      msg: \"Invalid Collection Trade State Identifier\",\n    },\n    {\n      code: 6029,\n      name: \"BumpSeedNotInHashMap\",\n      msg: \"Bump seed not in hash map.\",\n    },\n    {\n      code: 6030,\n      name: \"MetaplexTransferFailed\",\n      msg: \"Failed to transfer NFT using metaplex\",\n    },\n    {\n      code: 6031,\n      name: \"MetaplexDelegateFailed\",\n      msg: \"Failed to set Sale delegate on NFT using metaplex\",\n    },\n    {\n      code: 6032,\n      name: \"MetaplexRevokeFailed\",\n      msg: \"Failed to revoke Sale delegate on NFT using metaplex\",\n    },\n    {\n      code: 6033,\n      name: \"IncorrectTokenStandard\",\n      msg: \"Token standard must be Programmable NFT or Standard NFT\",\n    },\n    {\n      code: 6034,\n      name: \"PNFTDelegateSetAlready\",\n      msg: \"PNFT Delegate set already\",\n    },\n    {\n      code: 6035,\n      name: \"RoyaltyBPMismatch\",\n      msg: \"Royalty basis points mismatch\",\n    },\n  ],\n};\n"
  },
  {
    "path": "chatgpt-plugin/src/lib/middleware.ts",
    "content": "import { NextApiRequest, NextApiResponse, NextApiHandler } from \"next\";\nimport { resolveAddress, resolveToken } from \"./address\";\n\nexport type Options = {\n  addresses?: string[];\n  tokens?: string[];\n  rewriteQuery?: boolean;\n};\n\nexport function makeApiPostRequest(handler: NextApiHandler, options?: Options): NextApiHandler {\n  return async (req: NextApiRequest, res: NextApiResponse) => {\n    const rewriteQuery = options ? options.rewriteQuery : false;\n\n    if (req.method != \"POST\") {\n      res.status(405).send({ message: \"Only POST requests allowed\" });\n      return;\n    }\n\n    const target = rewriteQuery ? req.query : req.body;\n    try {\n      if (options && options.addresses) {\n        for (const addr of options.addresses) {\n          const publicKey = await resolveAddress(target[addr]);\n          target[addr] = publicKey;\n        }\n      }\n      if (options && options.tokens) {\n        for (const token of options.tokens) {\n          const publicKey = await resolveToken(target[token]);\n          target[token] = publicKey;\n        }\n      }\n    } catch (e) {\n      res.status(400).send({ message: (e as Error).toString() });\n      return;\n    }\n\n    await handler(req, res);\n  };\n}\n"
  },
  {
    "path": "chatgpt-plugin/src/lib/on-chain-metadata/index.ts",
    "content": "import * as anchor from \"@coral-xyz/anchor\";\nimport { Connection } from \"@solana/web3.js\";\n\ntype SolanaPayTx = {\n  transaction: string;\n  message?: string;\n};\n\n/**\n * Need this to fake a wallet with only a publicKey for the anchor provider\n */\nclass FakeWallet {\n  publicKey: anchor.web3.PublicKey;\n  constructor(publicKey: anchor.web3.PublicKey) {\n    this.publicKey = publicKey;\n  }\n\n  async signTransaction<\n    T extends anchor.web3.Transaction | anchor.web3.VersionedTransaction\n  >(tx: T): Promise<T> {\n    return tx;\n  }\n\n  async signAllTransactions<\n    T extends anchor.web3.Transaction | anchor.web3.VersionedTransaction\n  >(txs: T[]): Promise<T[]> {\n    return txs;\n  }\n}\n\nexport async function createWriteNFTMetadataTx(\n  connection: Connection,\n  owner: string,\n  metadata: Object\n): Promise<SolanaPayTx> {\n  const program = await anchor.Program.at(\n    process.env.ON_CHAIN_METADATA_PROGRAM as string,\n    new anchor.AnchorProvider(\n      connection,\n      new FakeWallet(new anchor.web3.PublicKey(owner)),\n      {}\n    )\n  );\n\n  let metadataBytes = JSON.stringify(metadata);\n  console.log(metadataBytes.length);\n  if (metadataBytes.length > 500) {\n    throw new Error(\"Metadata too large: \" + metadataBytes.length);\n  }\n\n  const metadataKp = anchor.web3.Keypair.generate();\n\n  // Create metadata\n  let initIx = await program.methods\n    .initialize(new anchor.BN(metadataBytes.length))\n    .accounts({\n      owner,\n      metadata: metadataKp.publicKey,\n    })\n    .signers([metadataKp])\n    .instruction();\n\n  // Write metadata\n  let writeIx = await program.methods\n    .write(new anchor.BN(0), Buffer.from(metadataBytes))\n    .accounts({\n      metadata: metadataKp.publicKey,\n      owner,\n    })\n    .instruction();\n\n  // Validate metadata\n  let validateIx = await program.methods\n    .validate()\n    .accounts({ metadata: metadataKp.publicKey })\n    .instruction();\n\n  let tx = new anchor.web3.Transaction();\n  tx = tx.add(initIx).add(writeIx).add(validateIx);\n  tx.recentBlockhash = (await connection.getRecentBlockhash()).blockhash;\n  tx.feePayer = new anchor.web3.PublicKey(owner);\n  tx.partialSign(metadataKp);\n\n  return {\n    transaction: tx\n      .serialize({ requireAllSignatures: false })\n      .toString(\"base64\"),\n  };\n}\n\nexport async function createCloseNFTMetadataTx(\n  connection: Connection,\n  owner: string,\n  account: string\n): Promise<SolanaPayTx> {\n  const program = await anchor.Program.at(\n    process.env.ON_CHAIN_METADATA_PROGRAM as string,\n    new anchor.AnchorProvider(\n      connection,\n      new FakeWallet(new anchor.web3.PublicKey(owner)),\n      {}\n    )\n  );\n  let tx = await program.methods\n    .close()\n    .accounts({\n      metadata: account,\n      recipient: owner,\n      owner,\n    })\n    .transaction();\n\n  tx.recentBlockhash = (await connection.getRecentBlockhash()).blockhash;\n  tx.feePayer = new anchor.web3.PublicKey(owner);\n\n  return {\n    transaction: tx\n      .serialize({ requireAllSignatures: false })\n      .toString(\"base64\"),\n  };\n}\n"
  },
  {
    "path": "chatgpt-plugin/src/pages/api/constants.ts",
    "content": "import express from \"express\";\n\nimport { RestClient } from \"@hellomoon/api\";\nimport { Connection } from \"@solana/web3.js\";\nimport { HyperspaceClient } from \"hyperspace-client-js\";\n\nexport const APP = express();\nexport const PORT = process.env.PORT || 3333;\n\nimport dotenv from \"dotenv\";\ndotenv.config();\n\n// Internal Solana Pay constants\nexport const SOLANA_PAY_LABEL = \"Solana Labs ChatGPT Plugin\";\nexport const TRANSACTION_ENDPOINTS = [\n  \"createBuyNFT\",\n  \"createWriteNFTMetadata\",\n  \"createCloseNFTMetadata\",\n];\nexport type TransactionEndpoints = (typeof TRANSACTION_ENDPOINTS)[number];\nexport const TX_DESCRIPTIONS: Record<TransactionEndpoints, string> = {\n  createBuyNFT: \"Sign to Buy NFT\",\n  createWriteNFTMetadata: \"Sign to Write NFT Metadata\",\n  createCloseNFTMetadata: \"Sign to Close NFT Metadata\",\n  createTransferToken: \"Sign to Transfer Token\",\n  createTransferSol: \"Sign to Transfer Sol\",\n};\n\n// Inferred Constants\nexport let HELIUS_URL: string;\nexport let SELF_URL: string;\nexport let HYPERSPACE_CLIENT: HyperspaceClient;\nexport let HELLOMOON_CLIENT: RestClient;\nexport let CONNECTION: Connection;\n\nexport default function index() {\n  HELIUS_URL = `https://rpc.helius.xyz/?api-key=${process.env.HELIUS_API_KEY}`;\n  CONNECTION = new Connection(HELIUS_URL);\n  SELF_URL = process.env.SELF_URL as string;\n\n  HYPERSPACE_CLIENT = new HyperspaceClient(process.env.HYPERSPACE_API_KEY as string);\n\n  HELLOMOON_CLIENT = new RestClient(process.env.HELLOMOON_API_KEY as string);\n}\n"
  },
  {
    "path": "chatgpt-plugin/src/pages/api/handlers/getAccountInfo/index.ts",
    "content": "import { NextApiRequest, NextApiResponse } from \"next\";\nimport { PublicKey, Connection, Keypair } from \"@solana/web3.js\";\nimport { Program, AnchorProvider, BorshAccountsCoder, BN } from \"@coral-xyz/anchor\";\nimport NodeWallet from \"@coral-xyz/anchor/dist/cjs/nodewallet\";\n\nimport configConstants, { CONNECTION } from \"../../constants\";\nimport { makeApiPostRequest } from \"@/lib/middleware\";\nconfigConstants();\n\n/**\n * Replace Anchor data (BNs, PublicKeys) with stringified data\n * @param obj\n * @returns\n */\nexport function stringifyAnchorObject(obj: any): any {\n  if (obj === true || obj === false) {\n    return obj;\n  } else if (!obj) {\n    return {};\n  } else if (obj instanceof BN) {\n    return obj.toString();\n  } else if (obj instanceof PublicKey) {\n    return obj.toString();\n  } else if (typeof obj === \"bigint\") {\n    let bigInt = obj as BigInt;\n    return bigInt.toString();\n  }\n\n  if (typeof obj === \"object\" && obj !== null) {\n    return Object.keys(obj).reduce((acc: Record<string, any>, key: string) => {\n      acc[key] = stringifyAnchorObject(obj[key]);\n      return acc;\n    }, {});\n  }\n\n  return obj;\n}\n\n/**\n * Returns accountInfo or extends it with deserialized account data if the account is a program account of an Anchor program\n * @param accountAddress\n * @returns\n */\nasync function getParsedAccountInfo(connection: Connection, accountAddress: PublicKey) {\n  // TODO: copy the explorer code here that manually deserializes a bunch of stuff, like Mango & Pyth\n\n  const accountInfo = (await connection.getAccountInfo(accountAddress)) as any;\n  // If acccount is not a program, check for Anchor IDL\n  if (accountInfo?.owner && !accountInfo.executable) {\n    try {\n      const program = await Program.at(\n        accountInfo.owner,\n        new AnchorProvider(connection, new NodeWallet(Keypair.generate()), {\n          commitment: \"confirmed\",\n        }),\n      );\n\n      // Search through Anchor IDL for the account type\n      const rawData = accountInfo.data;\n      const coder = new BorshAccountsCoder(program.idl);\n      const accountDefTmp = program.idl.accounts?.find((accountType: any) =>\n        (rawData as Buffer)\n          .slice(0, 8)\n          .equals(BorshAccountsCoder.accountDiscriminator(accountType.name)),\n      );\n\n      // If we found the Anchor IDL type, decode the account state\n      if (accountDefTmp) {\n        const accountDef = accountDefTmp;\n\n        // Decode the anchor data & stringify the data\n        const decodedAccountData = stringifyAnchorObject(coder.decode(accountDef.name, rawData));\n\n        let payload = {\n          ...accountInfo,\n          extended: decodedAccountData,\n        };\n        delete payload[\"data\"];\n        return payload;\n      }\n    } catch (err) {\n      console.log(err);\n    }\n  }\n  return accountInfo || {};\n}\n\nasync function handler(req: NextApiRequest, res: NextApiResponse) {\n  const accountInfo = await getParsedAccountInfo(CONNECTION, req.body[\"address\"]);\n  res.status(200).json(accountInfo);\n}\n\nexport default makeApiPostRequest(handler, { addresses: [\"address\"] });\n"
  },
  {
    "path": "chatgpt-plugin/src/pages/api/handlers/getAssetsByOwner/index.ts",
    "content": "import { NextApiRequest, NextApiResponse } from \"next\";\nimport configConstants, { HELIUS_URL } from \"../../constants\";\nconfigConstants();\n\nimport axios from \"axios\";\nimport { makeApiPostRequest } from \"@/lib/middleware\";\n\n/**\n * Returns the data from the Metaplex Read API\n * @param address\n * @param page (optional) page number\n * @param limit (optional) set to 5 to prevent overflowing GPT context window\n * @returns\n */\nexport const readApiGetAssetsByOwner = async (\n  address: string,\n  page: number = 1,\n  limit: number = 5,\n) => {\n  const sortBy = {\n    sortBy: \"created\",\n    sortDirection: \"asc\",\n  };\n  const before = \"\";\n  const after = \"\";\n  const { data } = await axios.post(HELIUS_URL, {\n    jsonrpc: \"2.0\",\n    id: \"my-id\",\n    method: \"getAssetsByOwner\",\n    params: [address, sortBy, limit, page, before, after],\n  });\n  return data.result;\n};\n\nasync function handler(req: NextApiRequest, res: NextApiResponse) {\n  const assets = await readApiGetAssetsByOwner(req.body[\"address\"].toString());\n  res.status(200).send(assets);\n}\n\nexport default makeApiPostRequest(handler, { addresses: [\"address\"] });\n"
  },
  {
    "path": "chatgpt-plugin/src/pages/api/handlers/getBalance/index.ts",
    "content": "import { NextApiRequest, NextApiResponse } from \"next\";\nimport { PublicKey, LAMPORTS_PER_SOL } from \"@solana/web3.js\";\nimport configConstants, { CONNECTION } from \"../../constants\";\nconfigConstants();\nimport { makeApiPostRequest } from \"@/lib/middleware\";\n\nasync function handler(req: NextApiRequest, res: NextApiResponse) {\n  const { address } = req.body;\n  const balance = await CONNECTION.getBalance(new PublicKey(address));\n  res.status(200).send({ sol: balance / LAMPORTS_PER_SOL });\n}\n\nexport default makeApiPostRequest(handler, { addresses: [\"address\"] });\n"
  },
  {
    "path": "chatgpt-plugin/src/pages/api/handlers/getCollectionsByFloorPrice/index.ts",
    "content": "import { NextApiRequest, NextApiResponse } from \"next\";\nimport { bs58 } from \"@coral-xyz/anchor/dist/cjs/utils/bytes\";\nimport configConstants, { HYPERSPACE_CLIENT } from \"../../constants\";\nimport { makeApiPostRequest } from \"@/lib/middleware\";\nconfigConstants();\n\ntype CollectionStats = {\n  id: string;\n  desc: string;\n  img: string;\n  website: string;\n  floor_price: number;\n};\n\n/**\n * Provides a feed of NFT collections that the user can afford.\n */\nasync function hyperspaceGetCollectionsByFloorPrice(\n  maxFloorPrice: number | undefined,\n  minFloorPrice: number | undefined,\n  pageSize: number = 5,\n  orderBy: string = \"DESC\",\n  humanReadableSlugs: boolean = false,\n) {\n  let pageNumber = 1;\n  let results: CollectionStats[] = [];\n  let hasMore = true;\n\n  const PAGE_SCRAPE_LIMIT = 10;\n  while (results.length < pageSize && pageNumber < PAGE_SCRAPE_LIMIT && hasMore) {\n    let projects = await HYPERSPACE_CLIENT.getProjects({\n      condition: {\n        floorPriceFilter: {\n          min: minFloorPrice ?? null,\n          max: maxFloorPrice ?? null,\n        },\n      },\n      orderBy: {\n        field_name: \"floor_price\",\n        sort_order: orderBy.toLocaleUpperCase() as any,\n      },\n      paginationInfo: {\n        page_size: 512,\n        page_number: pageNumber,\n      },\n    });\n\n    let stats: CollectionStats[] =\n      projects.getProjectStats.project_stats\n        ?.filter(project => {\n          return (project.volume_7day ?? 0 > 0) && (project.floor_price ?? 0 > 0);\n        })\n        .map(project => {\n          return {\n            id: project.project_id,\n            desc: project.project?.display_name ?? \"\",\n            img: project.project?.img_url ?? \"\",\n            website: project.project?.website ?? \"\",\n            floor_price: project.floor_price ?? 0,\n          };\n        }) ?? [];\n\n    if (humanReadableSlugs) {\n      stats = stats?.filter(stat => {\n        try {\n          bs58.decode(stat.id!);\n          return false;\n        } catch (err) {\n          return true;\n        }\n      });\n    }\n    pageNumber += 1;\n    console.log(\"\\tFetching collection info... \", stats.length, pageNumber);\n    results = results.concat(stats!);\n    hasMore = projects.getProjectStats.pagination_info.has_next_page;\n  }\n\n  return {\n    projects: results.slice(0, pageSize),\n    hasMore: hasMore,\n  };\n}\n\nasync function handler(req: NextApiRequest, res: NextApiResponse) {\n  const { maxFloorPrice, minFloorPrice, orderBy, pageSize, humanReadable } = req.body;\n  const result = await hyperspaceGetCollectionsByFloorPrice(\n    maxFloorPrice,\n    minFloorPrice,\n    pageSize,\n    orderBy,\n    humanReadable,\n  );\n  res.status(200).send(JSON.stringify(result));\n}\n\nexport default makeApiPostRequest(handler);\n"
  },
  {
    "path": "chatgpt-plugin/src/pages/api/handlers/getCollectionsByName/index.ts",
    "content": "import { NextApiRequest, NextApiResponse } from \"next\";\nimport configConstants, { HYPERSPACE_CLIENT } from \"../../constants\";\nconfigConstants();\nimport { makeApiPostRequest } from \"@/lib/middleware\";\n\nasync function getCollectionsByName(req: NextApiRequest, res: NextApiResponse) {\n  const { projectName } = req.body;\n  const projects = await HYPERSPACE_CLIENT.searchProjectByName({\n    condition: {\n      // In practice, it was harder to find projects based on this condition\n      //   matchName: {\n      //     operation: \"FUZZY\" as StringInputOperationEnum.Fuzzy,\n      //     value: projectName as string,\n      //   },\n      name: projectName as string,\n    },\n  });\n  const paredResponse =\n    projects.getProjectStatByName.project_stats\n      ?.map(project => {\n        return {\n          id: project.project_id,\n          verified: project.project?.is_verified ?? false,\n          name: project.project?.display_name ?? \"\",\n          img: project.project?.img_url ?? \"\",\n          website: project.project?.website ?? \"\",\n          floorPrice: project.floor_price ?? 0,\n          twitterFollowers: project.twitter_followers ?? 0,\n          twitter: project.project?.twitter ?? \"\",\n        };\n      })\n      .filter(project => project.verified) ?? [];\n  const result = {\n    projects: paredResponse,\n  };\n\n  res.status(200).send(JSON.stringify(result));\n}\n\nexport default makeApiPostRequest(getCollectionsByName);\n"
  },
  {
    "path": "chatgpt-plugin/src/pages/api/handlers/getListedCollectionNFTs/index.ts",
    "content": "import { NextApiRequest, NextApiResponse } from \"next\";\nimport configConstants, { HYPERSPACE_CLIENT } from \"../../constants\";\nimport { makeApiPostRequest } from \"@/lib/middleware\";\nconfigConstants();\n\ntype NFTListing = {\n  price: number;\n  image: string;\n  token: string;\n};\n\ntype ListedNFTResponse = {\n  listings: NFTListing[];\n  hasMore: boolean;\n};\n\nasync function hyperspaceGetListedCollectionNFTs(\n  projectId: string,\n  pageSize: number = 5,\n  priceOrder: string = \"DESC\",\n): Promise<ListedNFTResponse> {\n  let listedNFTs: NFTListing[] = [];\n  let hasMore = true;\n  let pageNumber = 1;\n  let pagesScraped = 0;\n  const PAGE_SCRAPE_LIMIT = 10;\n  while (listedNFTs.length < pageSize && hasMore && pagesScraped < PAGE_SCRAPE_LIMIT) {\n    let results = await HYPERSPACE_CLIENT.getMarketplaceSnapshot({\n      condition: {\n        projects: [{ project_id: projectId }],\n        onlyListings: true,\n      },\n      orderBy: {\n        field_name: \"lowest_listing_price\",\n        sort_order: priceOrder.toLocaleUpperCase() as any,\n      },\n      paginationInfo: {\n        page_number: pageNumber,\n      },\n    });\n\n    let snaps = results.getMarketPlaceSnapshots.market_place_snapshots!;\n    let orderedListings = snaps.sort(\n      (a, b) => a.lowest_listing_mpa!.price! - b.lowest_listing_mpa!.price!,\n    );\n\n    pageNumber += 1;\n    let crucialInfo: NFTListing[] = orderedListings\n      .filter(\n        arr =>\n          // We filter out Magic Eden's marketplace because they\n          // require an API key to make purchases programmatically\n          arr.lowest_listing_mpa?.marketplace_program_id !==\n          \"M2mx93ekt1fmXSVkTrUL9xVFHkmME8HTUi5Cyc5aF7K\",\n      )\n      .map(arr => {\n        return {\n          price: arr.lowest_listing_mpa!.price!,\n          token: arr.token_address,\n          image: arr.meta_data_img ?? \"\",\n          marketplace: arr.lowest_listing_mpa!.marketplace_program_id!,\n        };\n      });\n    listedNFTs = listedNFTs.concat(crucialInfo);\n    hasMore = results.getMarketPlaceSnapshots.pagination_info.has_next_page;\n  }\n\n  return {\n    listings: listedNFTs.slice(0, pageSize),\n    hasMore,\n  };\n}\n\nasync function handler(req: NextApiRequest, res: NextApiResponse) {\n  const { projectId, pageSize, priceOrder } = req.body;\n  const result = await hyperspaceGetListedCollectionNFTs(projectId, pageSize, priceOrder);\n  res.status(200).send(JSON.stringify(result));\n}\n\nexport default makeApiPostRequest(handler);\n"
  },
  {
    "path": "chatgpt-plugin/src/pages/api/handlers/getSignaturesForAddress/index.ts",
    "content": "import { NextApiRequest, NextApiResponse } from \"next\";\nimport { PublicKey } from \"@solana/web3.js\";\nimport configConstants, { CONNECTION } from \"../../constants\";\nimport { makeApiPostRequest } from \"@/lib/middleware\";\nconfigConstants();\n\nasync function handler(req: NextApiRequest, res: NextApiResponse) {\n  const accountAddress = req.body[\"address\"];\n\n  const beforeSig = req.body.beforeSignature ?? \"\";\n  const untilSig = req.body.untilSignature ?? \"\";\n  const signatures = await CONNECTION.getSignaturesForAddress(accountAddress, {\n    limit: 11,\n    before: beforeSig.length > 0 ? beforeSig : undefined,\n    until: untilSig.length > 0 ? untilSig : undefined,\n  });\n  res.status(200).send({\n    hasMore: signatures.length === 11,\n    nextPage: signatures.length === 11 ? { beforeSignature: signatures[10].signature } : null,\n    signatures: signatures.map(sig => {\n      return {\n        slot: sig.slot,\n        signature: sig.signature,\n        err: sig.err ?? undefined,\n        memo: sig.memo ?? undefined,\n        confirmationStatus: sig.confirmationStatus,\n        blockTime: new Date((sig.blockTime as number) * 1000).toDateString(),\n      };\n    }),\n  });\n}\n\nexport default makeApiPostRequest(handler, { addresses: [\"address\"] });\n"
  },
  {
    "path": "chatgpt-plugin/src/pages/api/handlers/getTokenAccounts/index.ts",
    "content": "import { NextApiRequest, NextApiResponse } from \"next\";\nimport { TOKEN_PROGRAM_ID } from \"@solana/spl-token\";\nimport configConstants, { CONNECTION } from \"../../constants\";\nimport { makeApiPostRequest } from \"@/lib/middleware\";\nconfigConstants();\n\ntype TokenInfo = {\n  mint: string;\n  amount: string;\n};\n\nasync function handler(req: NextApiRequest, res: NextApiResponse) {\n  let result = await CONNECTION.getParsedTokenAccountsByOwner(\n    req.body[\"address\"],\n    { programId: TOKEN_PROGRAM_ID },\n    \"confirmed\",\n  );\n  const tokenInfos: TokenInfo[] = [];\n  for (const accountInfo of result.value) {\n    const info = accountInfo.account.data.parsed.info;\n    if (info.tokenAmount.uiAmount !== 0) {\n      tokenInfos.push({\n        mint: info.mint.toString(),\n        amount: info.tokenAmount.uiAmountString,\n      });\n    }\n  }\n  res.status(200).json(tokenInfos);\n}\n\nexport default makeApiPostRequest(handler, { addresses: [\"address\"] });\n"
  },
  {
    "path": "chatgpt-plugin/src/pages/api/handlers/getTotalValue/index.ts",
    "content": "import { TokenPriceBatchedRequest, NftMintPriceByCreatorAvgRequest } from \"@hellomoon/api\";\nimport { PublicKey } from \"@solana/web3.js\";\nimport { NextApiRequest, NextApiResponse } from \"next\";\n\nimport { readApiGetAssetsByOwner } from \"../getAssetsByOwner\";\nimport configConstants, { CONNECTION, HELLOMOON_CLIENT } from \"../../constants\";\nimport { makeApiPostRequest } from \"@/lib/middleware\";\nconfigConstants();\n\nasync function getTokenTotal(address: string) {\n  let connection = CONNECTION;\n  let tokenInfos = await (\n    await fetch(\n      `https://api.helius.xyz/v0/addresses/${address}/balances?api-key=${process.env.HELIUS_API_KEY}`,\n    )\n  ).json();\n\n  let mints: string[] = [];\n  let mintAmount: Record<string, number> = {};\n  for (const info of tokenInfos[\"tokens\"] as any) {\n    if (info.amount !== \"0\") {\n      mints.push(info.mint);\n      mintAmount[info.mint] = Number.parseInt(info.amount);\n    }\n  }\n\n  let mintPrice: Record<string, number> = {};\n  let mintVolume: Record<string, number> = {};\n\n  let pricePromises: Promise<any>[] = [];\n  let decimalPromises: Promise<any>[] = [];\n  let counter = 0;\n  let batchSize = 10;\n  while (counter < mints.length) {\n    pricePromises.push(\n      HELLOMOON_CLIENT.send(\n        new TokenPriceBatchedRequest({ mints: mints.slice(counter, counter + batchSize) }),\n      ),\n    );\n\n    for (let i = counter; i < Math.min(counter + batchSize, mints.length); i++) {\n      decimalPromises.push(connection.getParsedAccountInfo(new PublicKey(mints[i])));\n    }\n    counter += batchSize;\n  }\n  let allData: any[] = [];\n  try {\n    allData = await Promise.all([...decimalPromises, ...pricePromises]);\n  } catch (e) {\n    console.error(\"Failed to getParsedAccountInfo:\", e);\n    throw e;\n  }\n  let decimalData = allData.slice(0, mints.length);\n  let mintDecimals: Record<string, number> = {};\n  decimalData.forEach((accountInfo, index) => {\n    mintDecimals[mints[index]] = accountInfo!.value!.data?.parsed?.info?.decimals;\n  });\n\n  let priceData = allData.slice(mints.length);\n\n  let tokenTotal = 0;\n  for (const mintPriceData of priceData) {\n    for (const price of (mintPriceData as any).data) {\n      if (price.price) {\n        let decimal = mintDecimals[price.mints];\n        mintPrice[price.mints] = price.price / 1e6;\n        mintVolume[price.mints] = price.volume;\n        tokenTotal += (mintPrice[price.mints] * mintAmount[price.mints]) / Math.pow(10, decimal);\n      }\n    }\n  }\n  return (Number(tokenTotal * 100).toFixed(1) as any as number) / 100;\n}\n\nasync function getNFTTotal(address: string) {\n  let assets = ((await readApiGetAssetsByOwner(address, 1, 100)) as any).items;\n  assets = assets.filter((asset: any) => asset.grouping.length > 0);\n\n  let groupings: Record<string, number> = {};\n  for (const asset of assets) {\n    if (asset.grouping.length === 0) {\n      continue;\n    }\n    let grouping = asset.grouping[0];\n    let collection = grouping.group_value;\n    groupings[collection] = (groupings[collection] || 0) + 1;\n  }\n\n  let assetIds = assets.map((asset: any) => asset.id);\n  let nftPricePromises = assetIds.map((id: string) => {\n    return HELLOMOON_CLIENT.send(\n      new NftMintPriceByCreatorAvgRequest({\n        nftMint: id,\n      }),\n    ).then(price => price.data[0]?.avg_usd_price ?? 0);\n  });\n  let prices = await Promise.all(nftPricePromises);\n\n  let nftTotal: number = prices.reduce((a, b) => a + b, 0);\n  return (Number(nftTotal * 100).toFixed(1) as any as number) / 100;\n}\n\nasync function handler(req: NextApiRequest, res: NextApiResponse) {\n  const address = req.body[\"address\"];\n\n  let totals = await Promise.all([\n    getTokenTotal(address.toString()),\n    getNFTTotal(address.toString()),\n  ]);\n  let [tokenTotal, nftTotal] = totals;\n  res.status(200).json({ tokenTotal, nftTotal, total: tokenTotal + nftTotal });\n}\n\nexport default makeApiPostRequest(handler, { addresses: [\"address\"] });\n"
  },
  {
    "path": "chatgpt-plugin/src/pages/api/handlers/getTransaction/index.ts",
    "content": "import { NextApiRequest, NextApiResponse } from \"next\";\nimport configConstants, { CONNECTION } from \"../../constants\";\nconfigConstants();\n\nimport * as anchor from \"@coral-xyz/anchor\";\nimport NodeWallet from \"@coral-xyz/anchor/dist/cjs/nodewallet\";\nimport { stringifyAnchorObject } from \"../getAccountInfo\";\nimport {\n  AccountMeta,\n  ComputeBudgetInstruction,\n  ComputeBudgetInstructionType,\n  ComputeBudgetProgram,\n  LAMPORTS_PER_SOL,\n  SystemInstruction,\n  SystemProgram,\n  TransactionError,\n  TransactionInstruction,\n} from \"@solana/web3.js\";\nimport {\n  TOKEN_PROGRAM_ID,\n  TOKEN_2022_PROGRAM_ID,\n  decodeInstruction,\n  TokenInstruction,\n} from \"@solana/spl-token\";\nimport { u8 } from \"@solana/buffer-layout\";\n\n// Setup Solflare\nimport { Client } from \"@solflare-wallet/utl-sdk\";\nimport { makeApiPostRequest } from \"@/lib/middleware\";\nconst utl = new Client();\n\ntype IxArgs = Record<string, string>;\n\ntype Instruction = {\n  programId: string;\n  keys: string[];\n  ixData: string | IxArgs;\n  depth: number;\n};\n\nlet PROGRAM_CACHE = new Map<string, anchor.Program | null>();\n\ntype ParsedTxResponse = {\n  // TODO(ngundotra): I'm pretty sure amount needs to be a string bc u57 overflow, but this is what SPL Token uses\n  tokenBalanceChanges: Record<string, { mint: string; amount: number }>;\n  solBalanceChanges: Record<string, number>;\n  instructions: Instruction[];\n  success: boolean;\n  timestamp: string;\n  feePayer: string;\n  err?: TransactionError;\n  // Dev Mode Only params\n  computeUnitsConsumed?: number;\n  txFeeInLamports?: number;\n  txVersion?: string;\n  slot?: number;\n  logs?: string[];\n};\n\nasync function getAnchorProgram(programId: string) {\n  if (PROGRAM_CACHE.has(programId)) {\n    return PROGRAM_CACHE.get(programId);\n  }\n\n  try {\n    const program = await anchor.Program.at(\n      programId,\n      new anchor.AnchorProvider(CONNECTION, new NodeWallet(anchor.web3.Keypair.generate()), {}),\n    );\n    PROGRAM_CACHE.set(programId, program);\n    return program;\n  } catch (err) {\n    PROGRAM_CACHE.set(programId, null);\n    return null;\n  }\n}\n\nasync function parseAnchorIxData(programId: anchor.web3.PublicKey, ixData: string) {\n  const program = await getAnchorProgram(programId.toString());\n  if (!program) {\n    return null;\n  }\n  let ixCoder = new anchor.BorshInstructionCoder(program.idl);\n  return ixCoder.decode(Buffer.from(ixData, \"base64\"));\n}\n\nasync function parseAnchorIxAccounts(\n  programAddress: string,\n  accounts: string[],\n  ix: anchor.Instruction,\n) {\n  const program = await getAnchorProgram(programAddress);\n\n  if (!program) {\n    return null;\n  }\n\n  const ixDef = program?.idl.instructions.find((ixDef: any) => ixDef.name === ix.name);\n  if (ixDef) {\n    let parsedAccounts = ixDef.accounts as {\n      // type coercing since anchor doesn't export the underlying type\n      name: string;\n      isMut: boolean;\n      isSigner: boolean;\n      pda?: object;\n    }[];\n\n    let allAccounts = parsedAccounts.map((acct, idx) => {\n      return JSON.stringify({\n        name: acct.name,\n        isMut: acct.isMut,\n        isSigner: acct.isSigner,\n        address: accounts[idx],\n      });\n    });\n    if (parsedAccounts && parsedAccounts.length < accounts.length) {\n      allAccounts = allAccounts.concat(accounts.slice(parsedAccounts.length));\n    }\n    return allAccounts;\n  }\n  return null;\n}\n\nexport function snakeToTitleCase(str: string): string {\n  const result = str.replace(/([-_]\\w)/g, g => ` ${g[1].toUpperCase()}`);\n  return result.charAt(0).toUpperCase() + result.slice(1);\n}\n\nasync function parseAnchorIx(ix: Instruction) {\n  let parsedIx = await parseAnchorIxData(\n    new anchor.web3.PublicKey(ix.programId),\n    ix.ixData as string,\n  );\n  if (!parsedIx) {\n    return ix;\n  }\n\n  let parsedAccounts = await parseAnchorIxAccounts(ix.programId, ix.keys, parsedIx);\n\n  let ixTitle = parsedIx.name;\n  ixTitle = ixTitle.charAt(0).toUpperCase() + ixTitle.slice(1);\n  let program = await getAnchorProgram(ix.programId);\n  return {\n    programId: `${snakeToTitleCase(program!.idl.name)} (${ix.programId})`,\n    ixData: `${ixTitle} ${JSON.stringify(stringifyAnchorObject(parsedIx.data))}`,\n    keys: parsedAccounts ?? ix.keys,\n    depth: ix.depth,\n  };\n}\n\nasync function parseIx(ix: TransactionInstruction, depth: number): Promise<Instruction> {\n  let programAddress = ix.programId.toBase58();\n\n  let parsedIx: Instruction = {\n    programId: programAddress,\n    keys: ix.keys.map(k => k.pubkey.toBase58()),\n    ixData: \"\",\n    depth,\n  };\n  if (programAddress === SystemProgram.programId.toBase58()) {\n    parsedIx.programId = \"System Program\";\n\n    let type = SystemInstruction.decodeInstructionType(ix);\n    switch (type) {\n      case \"AdvanceNonceAccount\":\n        parsedIx.ixData = stringifyAnchorObject({\n          AdvanceNonceAccount: SystemInstruction.decodeNonceAdvance(ix),\n        });\n        break;\n      case \"Allocate\":\n        parsedIx.ixData = stringifyAnchorObject({\n          Allocate: SystemInstruction.decodeAllocate(ix),\n        });\n        break;\n      case \"AllocateWithSeed\":\n        parsedIx.ixData = stringifyAnchorObject({\n          AllocateWithSeed: SystemInstruction.decodeAllocateWithSeed(ix),\n        });\n        break;\n      case \"Assign\":\n        parsedIx.ixData = stringifyAnchorObject({ Assign: SystemInstruction.decodeAssign(ix) });\n        break;\n      case \"AssignWithSeed\":\n        parsedIx.ixData = stringifyAnchorObject({\n          AssignWithSeed: SystemInstruction.decodeAssignWithSeed(ix),\n        });\n        break;\n      case \"AuthorizeNonceAccount\":\n        parsedIx.ixData = stringifyAnchorObject({\n          AuthorizeNonceAccount: SystemInstruction.decodeNonceAuthorize(ix),\n        });\n        break;\n      case \"Create\":\n        parsedIx.ixData = stringifyAnchorObject({\n          Create: SystemInstruction.decodeCreateAccount(ix),\n        });\n        break;\n      case \"CreateWithSeed\":\n        parsedIx.ixData = stringifyAnchorObject({\n          CreateWithSeed: SystemInstruction.decodeCreateWithSeed(ix),\n        });\n        break;\n      case \"InitializeNonceAccount\":\n        parsedIx.ixData = stringifyAnchorObject({\n          InitializeNonceAccount: SystemInstruction.decodeNonceInitialize(ix),\n        });\n        break;\n      case \"Transfer\":\n        parsedIx.ixData = stringifyAnchorObject({ Transfer: SystemInstruction.decodeTransfer(ix) });\n        break;\n      case \"TransferWithSeed\":\n        parsedIx.ixData = stringifyAnchorObject({\n          TransferWithSeed: SystemInstruction.decodeTransferWithSeed(ix),\n        });\n        break;\n      case \"WithdrawNonceAccount\":\n        parsedIx.ixData = stringifyAnchorObject({\n          WithdrawNonceAccount: SystemInstruction.decodeNonceWithdraw(ix),\n        });\n        break;\n      case \"UpgradeNonceAccount\":\n        break;\n    }\n  } else if (programAddress === TOKEN_2022_PROGRAM_ID.toBase58()) {\n    parsedIx.programId = \"SPL Token Program 2022\";\n\n    parsedIx.ixData = stringifyAnchorObject(await parseTokenInstruction(ix));\n  } else if (programAddress === TOKEN_PROGRAM_ID.toBase58()) {\n    parsedIx.programId = \"SPL Token Program\";\n\n    parsedIx.ixData = stringifyAnchorObject(await parseTokenInstruction(ix));\n  } else if (programAddress === ComputeBudgetProgram.programId.toBase58()) {\n    parsedIx.programId = \"Compute Budget Program\";\n    let type: ComputeBudgetInstructionType = ComputeBudgetInstruction.decodeInstructionType(ix);\n\n    switch (type) {\n      case \"RequestHeapFrame\":\n        parsedIx.ixData = stringifyAnchorObject({\n          RequestHeapFrame: ComputeBudgetInstruction.decodeRequestHeapFrame(ix),\n        });\n        break;\n      case \"RequestUnits\":\n        parsedIx.ixData = stringifyAnchorObject({\n          RequestUnits: ComputeBudgetInstruction.decodeRequestUnits(ix),\n        });\n        break;\n      case \"SetComputeUnitLimit\":\n        parsedIx.ixData = stringifyAnchorObject({\n          SetComputeUnitLimit: ComputeBudgetInstruction.decodeSetComputeUnitLimit(ix),\n        });\n        break;\n      case \"SetComputeUnitPrice\":\n        parsedIx.ixData = stringifyAnchorObject({\n          SetComputeUnit: ComputeBudgetInstruction.decodeSetComputeUnitPrice(ix),\n        });\n        break;\n    }\n  } else {\n    return await parseAnchorIx({\n      programId: programAddress,\n      ixData: anchor.utils.bytes.base64.encode(ix.data),\n      keys: ix.keys.map(k => k.pubkey.toBase58()),\n      depth,\n    });\n  }\n  return parsedIx;\n}\n\n/**\n * Flattens `keys` to just be the name of the account and the address\n * Extends `mint` addresses with the mint name (e.g. \"(USDC)\")\n */\nasync function parseTokenInstruction(ix: TransactionInstruction): Promise<Record<string, any>> {\n  let ixTypeByte = u8().decode(ix.data.slice(0, 1));\n  let ixName = TokenInstruction[ixTypeByte];\n  let decoded: Record<string, any> = {};\n  let decodedIx = decodeInstruction(ix);\n\n  let keys: Record<string, string> = {};\n  for (const keyName of Object.keys(decodedIx.keys)) {\n    const meta = (decodedIx.keys as Record<string, AccountMeta>)[keyName];\n    if (keyName === \"multiSigners\") {\n      continue;\n    }\n    let address = meta.pubkey.toBase58();\n\n    if (keyName === \"mint\") {\n      let mintData = await utl.fetchMint(meta.pubkey);\n      address = `${mintData.symbol}`;\n    }\n    keys[keyName] = address;\n  }\n  // @ts-ignore\n  decodedIx.keys = keys;\n\n  if (decodedIx.data && \"amount\" in decodedIx.data) {\n    let amount: anchor.BN;\n    if (typeof decodedIx.data.amount === \"bigint\") {\n      amount = new anchor.BN(decodedIx.data.amount.toString());\n    } else {\n      amount = new anchor.BN(decodedIx.data.amount);\n    }\n\n    // Todo(ngundotra): add support for decimals\n    if (\"decimals\" in decodedIx.data) {\n      let decimals = decodedIx.data.decimals;\n      amount = amount.div(new anchor.BN(10).pow(new anchor.BN(decimals)));\n\n      // @ts-ignore\n      delete decodedIx.data[\"decimals\"];\n    }\n\n    // @ts-ignore\n    decodedIx.data.amount = amount;\n  }\n\n  decoded[ixName] = decodedIx;\n  return decoded;\n}\n\nasync function parseTokenChanges(\n  preBalances: anchor.web3.ConfirmedTransactionMeta[\"preTokenBalances\"],\n  postBalances: anchor.web3.ConfirmedTransactionMeta[\"postTokenBalances\"],\n) {\n  let mints = new Set([...preBalances!.map(b => b.mint), ...postBalances!.map(b => b.mint)]);\n  let mintData = await utl.fetchMints(\n    Array.from(mints.keys()).map(k => new anchor.web3.PublicKey(k)),\n  );\n  let mintMap: Record<string, string> = {};\n  mintData\n    .filter(token => token.verified ?? true)\n    .forEach(m => {\n      mintMap[m.address.toString()] = m.symbol;\n    });\n\n  if (!postBalances || !preBalances || postBalances.length != preBalances.length) {\n    return null;\n  }\n\n  let changes: Record<string, { mint: string; amount: number }> = {};\n  for (let i = 0; i < postBalances?.length; i++) {\n    let pre = preBalances[i];\n    let post = postBalances[i];\n    let tokenChange = (post.uiTokenAmount.uiAmount ?? 0) - (pre.uiTokenAmount.uiAmount ?? 0);\n    if (tokenChange !== 0) {\n      changes[pre.owner!] = { mint: mintMap[pre.mint] ?? pre.mint, amount: tokenChange };\n    }\n  }\n  return changes;\n}\n\nfunction parseSolChanges(\n  accounts: anchor.web3.PublicKey[],\n  preBalances: number[],\n  postBalances: number[],\n) {\n  if (preBalances.length != postBalances.length) {\n    return null;\n  }\n  const changes: Record<string, number> = {};\n  for (let i = 0; i < preBalances.length; i++) {\n    let pre = preBalances[i];\n    let post = postBalances[i];\n    let solChange = post - pre;\n    if (solChange > 0.0000000001) {\n      changes[accounts[i].toString()] = solChange / LAMPORTS_PER_SOL;\n    }\n  }\n  return changes;\n}\n\nfunction parseLogs(logs: string[]): { programId: string; depth: number }[] {\n  let traces: { programId: string; depth: number }[] = [];\n  for (const log of logs) {\n    let match = log.match(/Program (.*) invoke \\[(.*)\\]/);\n    if (match) {\n      let program = match[1];\n      let depth = match[2];\n      traces.push({ programId: program, depth: Number.parseInt(depth) });\n    }\n  }\n  return traces;\n}\n\n// TODO(ngundotra): add support for System program + SPL programs\nasync function handler(req: NextApiRequest, res: NextApiResponse) {\n  const signature = req.body.signature;\n  const devMode = req.body.devMode ?? false;\n  const transaction = await CONNECTION.getTransaction(signature, {\n    maxSupportedTransactionVersion: 2,\n  });\n\n  if (!transaction) {\n    res.status(404).send(\"Transaction not found\");\n  }\n\n  let instructions: Instruction[] = [];\n\n  // Reconstruct account array\n  let accounts = transaction?.transaction.message.staticAccountKeys ?? [];\n  accounts = accounts.concat(transaction?.meta?.loadedAddresses?.readonly ?? []);\n  accounts = accounts.concat(transaction?.meta?.loadedAddresses?.writable ?? []);\n\n  let tokenBalanceChanges = await parseTokenChanges(\n    transaction?.meta?.preTokenBalances,\n    transaction?.meta?.postTokenBalances,\n  );\n  let solChanges = parseSolChanges(\n    accounts,\n    transaction?.meta?.preBalances ?? [],\n    transaction?.meta?.postBalances ?? [],\n  );\n\n  // Keep track of order of instructions (may not be able get stack depth for each inner ix because of log truncation)\n  let traceIdx = 0;\n  let parsedTrace = parseLogs(transaction?.meta?.logMessages ?? []);\n\n  // Loop through top-level instructions\n  let outerIdx = 0;\n  let innerIdx = 0;\n  for (const outerIx of transaction?.transaction.message.compiledInstructions ?? []) {\n    const programId = accounts[outerIx.programIdIndex];\n    instructions.push(\n      await parseIx(\n        {\n          programId,\n          data: Buffer.from(outerIx.data ?? []),\n          keys: outerIx.accountKeyIndexes.map(idx => {\n            return {\n              pubkey: accounts[idx],\n              isSigner: false,\n              isWritable: false,\n            };\n          }),\n        },\n        0,\n      ),\n    );\n    traceIdx += 1;\n\n    // Loop through inner instructions\n    let innerIxBucket = transaction?.meta?.innerInstructions?.[innerIdx];\n    if (innerIxBucket && innerIxBucket.index === outerIdx) {\n      for (const innerIx of innerIxBucket.instructions) {\n        if (parsedTrace[traceIdx].programId !== accounts[innerIx.programIdIndex].toBase58()) {\n          // Make a note that the depth is unknown (somehow)\n          parsedTrace[traceIdx].depth = 1;\n        }\n\n        instructions.push(\n          await parseIx(\n            {\n              programId: accounts[innerIx.programIdIndex],\n              keys: innerIx.accounts.map(key => {\n                return {\n                  pubkey: accounts[key],\n                  isSigner: false,\n                  isWritable: false,\n                };\n              }),\n              data: anchor.utils.bytes.bs58.decode(innerIx.data),\n            },\n            parsedTrace[traceIdx].depth,\n          ),\n        );\n        traceIdx += 1;\n      }\n      innerIdx += 1;\n    }\n\n    outerIdx += 1;\n  }\n  const feePayer = transaction?.transaction.message.staticAccountKeys[0]!.toBase58();\n\n  let timestamp: Date | undefined;\n  if (transaction?.blockTime) {\n    timestamp = new Date();\n    timestamp.setTime(transaction?.blockTime! * 1000);\n  }\n\n  let response: ParsedTxResponse = {\n    err: transaction?.meta?.err ?? undefined,\n    feePayer: feePayer!,\n    instructions,\n    tokenBalanceChanges: tokenBalanceChanges!,\n    solBalanceChanges: solChanges!,\n    success: transaction?.meta?.err === null,\n    timestamp: timestamp?.toISOString() ?? \"unknown\",\n  };\n  if (devMode) {\n    response = {\n      ...response,\n      computeUnitsConsumed: transaction?.meta?.computeUnitsConsumed!,\n      txFeeInLamports: transaction?.meta?.fee!,\n      txVersion: transaction?.version?.toString() ?? \"legacy\",\n      logs: transaction?.meta?.logMessages ?? [],\n      slot: transaction?.slot!,\n    };\n  }\n\n  res.status(200).send(JSON.stringify(response));\n}\n\nexport default makeApiPostRequest(handler);\n"
  },
  {
    "path": "chatgpt-plugin/src/pages/api/handlers/solana-pay/page/[methodName].ts",
    "content": "/// DEPRECATED - NO LONGER IN USE\nimport { NextApiRequest, NextApiResponse } from \"next\";\nimport { encode } from \"querystring\";\n\nimport configConstants, { SELF_URL, TX_DESCRIPTIONS } from \"../../../constants\";\nconfigConstants();\n\nfunction createOpenGraphMetaPage(\n  methodName: string,\n  encoded: string,\n  description: string\n): string {\n  let qrCodeUri = new URL(\n    `${SELF_URL}/api/handlers/solana-pay/qr/${methodName}?${encoded}`\n  );\n  return `<html>\n    <meta property=\"og:title\" content=\"${description}\" />\n    <meta property=\"og:type\" content=\"website\" />\n    <meta property=\"og:url\" content=\"${SELF_URL}/page/${methodName}?${encoded}\" />\n    <meta property=\"og:image\" content=\"${qrCodeUri}\" />\n    </html>`;\n}\n\nexport default async function handler(\n  req: NextApiRequest,\n  res: NextApiResponse\n) {\n  const {\n    query: { methodName },\n  } = req;\n  console.log(\"OpenGraph metapage requested:\", methodName, req.query);\n\n  let description = TX_DESCRIPTIONS[methodName as string];\n  res\n    .status(200)\n    .send(\n      createOpenGraphMetaPage(\n        methodName as string,\n        encode(Object(req.query)),\n        description\n      )\n    );\n}\n"
  },
  {
    "path": "chatgpt-plugin/src/pages/api/handlers/solana-pay/qr/[methodName].ts",
    "content": "import { NextApiRequest, NextApiResponse } from \"next\";\nimport { encode } from \"querystring\";\n\nimport { encodeURL } from \"@solana/pay\";\nimport * as qrcode from \"qrcode\";\nimport sharp from \"sharp\";\n\nimport configConstants, {\n  SELF_URL,\n  SOLANA_PAY_LABEL,\n} from \"../../../constants\";\nconfigConstants();\n\nasync function createQRCodePng(\n  methodName: string,\n  encoded: string\n): Promise<Buffer> {\n  let uri = new URL(\n    `${SELF_URL}/api/handlers/solana-pay/sign/${methodName}?${encoded}`\n  );\n  let solanaPayUrl = encodeURL({\n    link: uri,\n    label: SOLANA_PAY_LABEL,\n  });\n\n  let dataUrl = await qrcode.toDataURL(solanaPayUrl.toString(), {\n    errorCorrectionLevel: \"H\",\n  });\n  const base64Data = dataUrl.replace(/^data:image\\/png;base64,/, \"\");\n  const imageBuffer = Buffer.from(base64Data, \"base64\");\n  return await sharp(imageBuffer)\n    .extend({\n      extendWith: \"background\",\n      background: \"#ffffff\",\n      left: 10,\n      right: 10,\n    })\n    .toBuffer();\n}\n\nexport default async function handler(\n  req: NextApiRequest,\n  res: NextApiResponse\n) {\n  const {\n    query: { methodName },\n  } = req;\n\n  console.log(\"QR code requested:\", methodName, req.query);\n\n  let buffer = await createQRCodePng(\n    methodName as string,\n    encode(Object(req.query))\n  );\n  res.setHeader(\"Content-Type\", \"image/png\");\n  res.setHeader(\"Content-Disposition\", \"inline\");\n  res.status(200).send(buffer);\n}\n"
  },
  {
    "path": "chatgpt-plugin/src/pages/api/handlers/solana-pay/sign/createBurnAsset.ts",
    "content": "// DEPRECATED - not in use\nimport { bs58 } from \"@coral-xyz/anchor/dist/cjs/utils/bytes\";\nimport { createBurnInstruction } from \"@metaplex-foundation/mpl-bubblegum\";\nimport {\n  SPL_ACCOUNT_COMPRESSION_PROGRAM_ID,\n  SPL_NOOP_PROGRAM_ID,\n} from \"@solana/spl-account-compression\";\nimport { PublicKey, Transaction } from \"@solana/web3.js\";\nimport { NextApiRequest } from \"next\";\nimport { makeRespondToSolanaPayGet, makeRespondToSolanaPayPost } from \".\";\nimport configConstants, { CONNECTION } from \"../../../constants\";\nimport { bufferToArray, getAsset, getAssetProof, getBubblegumAuthorityPDA } from \"../utils/helpers\";\nconfigConstants();\n\nexport async function createBurnAsset(req: NextApiRequest) {\n  const { assetId } = req.query;\n  const { account: owner } = req.body;\n  if (!owner || !assetId) {\n    throw new Error(\"Missing required parameters\");\n  }\n  let assetProof = await getAssetProof(assetId, CONNECTION.rpcEndpoint);\n  const rpcAsset = await getAsset(assetId, CONNECTION.rpcEndpoint);\n  const leafNonce = rpcAsset.compression.leaf_id;\n  let proofPath = assetProof.proof.map((node: string) => ({\n    pubkey: new PublicKey(node),\n    isSigner: false,\n    isWritable: false,\n  }));\n  const treeAuthority = await getBubblegumAuthorityPDA(new PublicKey(assetProof.tree_id));\n  const leafDelegate = rpcAsset.ownership.delegate\n    ? new PublicKey(rpcAsset.ownership.delegate)\n    : new PublicKey(rpcAsset.ownership.owner);\n\n  if (rpcAsset.ownership.owner !== owner) {\n    throw new Error(\n      `NFT is not owned by the expected owner. Expected ${new PublicKey(owner)} but got ${\n        rpcAsset.ownership.owner\n      }.`,\n    );\n  }\n  const burnIx = createBurnInstruction(\n    {\n      treeAuthority,\n      leafOwner: new PublicKey(rpcAsset.ownership.owner),\n      leafDelegate,\n      merkleTree: new PublicKey(assetProof.tree_id),\n      logWrapper: SPL_NOOP_PROGRAM_ID,\n      compressionProgram: SPL_ACCOUNT_COMPRESSION_PROGRAM_ID,\n      anchorRemainingAccounts: proofPath,\n    },\n    {\n      root: bufferToArray(bs58.decode(assetProof.root)),\n      dataHash: bufferToArray(bs58.decode(rpcAsset.compression.data_hash.trim())),\n      creatorHash: bufferToArray(bs58.decode(rpcAsset.compression.creator_hash.trim())),\n      nonce: leafNonce,\n      index: leafNonce,\n    },\n  );\n  const tx = new Transaction().add(burnIx);\n  tx.feePayer = new PublicKey(owner);\n  tx.recentBlockhash = (await CONNECTION.getLatestBlockhash()).blockhash;\n  return {\n    transaction: tx.serialize({ requireAllSignatures: false }).toString(\"base64\"),\n  };\n}\n\nexport default makeRespondToSolanaPayGet(makeRespondToSolanaPayPost(createBurnAsset));\n"
  },
  {
    "path": "chatgpt-plugin/src/pages/api/handlers/solana-pay/sign/createBuyNFT.ts",
    "content": "// DEPRECATED - not in use\nimport { NextApiRequest } from \"next\";\n\nimport { base64 } from \"@coral-xyz/anchor/dist/cjs/utils/bytes\";\nimport configConstants, { HYPERSPACE_CLIENT } from \"../../../constants\";\nconfigConstants();\nimport { makeRespondToSolanaPayPost, makeRespondToSolanaPayGet } from \".\";\n\nasync function hyperspaceCreateBuyTx(buyer: string, token: string, price: number) {\n  let transactionData = await HYPERSPACE_CLIENT.createBuyTx({\n    buyerAddress: buyer,\n    tokenAddress: token,\n    price: price,\n    // Take no fee on making tx for ChatGPT users\n    buyerBroker: \"\",\n    buyerBrokerBasisPoints: 0,\n  });\n  const txBytes = base64.encode(Buffer.from(transactionData.createBuyTx.stdBuffer!));\n\n  return {\n    transaction: txBytes,\n  };\n}\n\nexport async function createBuyNFT(req: NextApiRequest) {\n  const { token, price } = req.query;\n  const { account: buyer } = req.body;\n  return await hyperspaceCreateBuyTx(\n    buyer as string,\n    token as string,\n    Number.parseFloat(price as string),\n  );\n}\n\nexport default makeRespondToSolanaPayGet(makeRespondToSolanaPayPost(createBuyNFT));\n"
  },
  {
    "path": "chatgpt-plugin/src/pages/api/handlers/solana-pay/sign/createCloseNFTMetadata.ts",
    "content": "// DEPRECATED - not in use\nimport { NextApiRequest } from \"next\";\n\nimport { createCloseNFTMetadataTx } from \"@/lib/on-chain-metadata\";\nimport { CONNECTION } from \"../../../constants\";\nimport { makeRespondToSolanaPayGet, makeRespondToSolanaPayPost } from \".\";\n\nasync function createCloseNFTMetadata(req: NextApiRequest) {\n  const { account } = req.query;\n  const { account: owner } = req.body;\n  return await createCloseNFTMetadataTx(CONNECTION, owner as string, account as string);\n}\n\nexport default makeRespondToSolanaPayGet(makeRespondToSolanaPayPost(createCloseNFTMetadata));\n"
  },
  {
    "path": "chatgpt-plugin/src/pages/api/handlers/solana-pay/sign/createListNFT.ts",
    "content": "// DEPRECATED - not in use\n/**\n * Deprecated because Solana Pay compatible wallets block NFT/token delegation to prevent scams\n * This means we can't list via Solana Pay, but maybe we can get around this by using NFT AMMs\n * to sell at the floor.\n */\nimport { NextApiRequest } from \"next\";\n\nimport configConstants, { CONNECTION } from \"../../../constants\";\nconfigConstants();\n\nimport { makeRespondToSolanaPayPost, makeRespondToSolanaPayGet } from \".\";\nimport { AnchorProvider, BN, Program } from \"@coral-xyz/anchor\";\n\nimport { Hyperspace, hyperspaceIdl } from \"@/lib/hyperspace/idl/hyperspace\";\nimport {\n  Keypair,\n  LAMPORTS_PER_SOL,\n  PublicKey,\n  SYSVAR_CLOCK_PUBKEY,\n  SYSVAR_INSTRUCTIONS_PUBKEY,\n  SystemProgram,\n  Transaction,\n  TransactionInstruction,\n} from \"@solana/web3.js\";\nimport NodeWallet from \"@coral-xyz/anchor/dist/cjs/nodewallet\";\nimport {\n  findTokenRecordPda,\n  getAtaForMint,\n  getEditionDataAccount,\n  getHyperspaceProgramAsSigner,\n  getHyperspaceTradeState,\n  getMetadata,\n} from \"@/lib/hyperspace/account\";\nimport { TOKEN_PROGRAM_ID, getAccount } from \"@solana/spl-token\";\nimport {\n  Metadata,\n  PROGRAM_ID as TOKEN_METADATA_PROGRAM_ID,\n} from \"@metaplex-foundation/mpl-token-metadata\";\nimport { HYPERSPACE_ID, HYPERSPACE_MARKETPLACE_INSTANCE } from \"@/lib/hyperspace/constants\";\n\nconst AUTH_PROGRAM_ID = new PublicKey(\"auth9SigNpDKz4sJJ1DfCTuZrZNSAgh9sFD3rboVmgg\");\n\n// async function hyperspaceCreateListTx(\n//   seller: string,\n//   token: string,\n//   price: number\n// ) {\n//   console.log(seller, token, price);\n//   let transactionData = await HYPERSPACE_CLIENT.createListTx({\n//     sellerAddress: seller,\n//     tokenAddress: token,\n//     price: price,\n//     // Take no fee on making tx for ChatGPT users\n//     sellerBroker: \"\",\n//     sellerBrokerBasisPoints: 0,\n//   });\n\n//   console.log(transactionData);\n//   const txBytes = base64.encode(\n//     Buffer.from(transactionData.createListTx.stdBuffer!)\n//   );\n//   console.log(txBytes);\n\n//   return {\n//     transaction: txBytes,\n//   };\n// }\n\nexport const getPriceWithMantissa = async (\n  price: number,\n  mint: PublicKey,\n  walletKeyPair: any,\n  anchorProgram: Program<Hyperspace>,\n): Promise<number> => {\n  // // const token = new Token(\n  // //   anchorProgram.provider.connection,\n  // //   new PublicKey(mint),\n  // //   TOKEN_PROGRAM_ID,\n  // //   walletKeyPair\n  // // );\n\n  // let ata = getAssociatedTokenAddressSync(mint, walletKeypair);\n\n  // const mintInfo = await token.getMintInfo();\n\n  // const mantissa = 10 ** mintInfo.decimals;\n\n  // return Math.ceil(price * mantissa);\n  return 1;\n};\n\nasync function createTradeStateInstruction(\n  anchorProgram: Program<Hyperspace>,\n  isBuy: boolean,\n  tradeBump: number,\n  buyPrice: BN,\n  brokerBasisPoints: number,\n  royalty_basis_points = 0,\n  tokenSize: BN,\n  user: PublicKey,\n  broker: PublicKey,\n  tokenAccount: PublicKey,\n  tokenMint: PublicKey,\n  tradeStateAddress: PublicKey,\n): Promise<TransactionInstruction> {\n  console.log(`Create instruction for creating trade state with address ${tradeStateAddress}\n      for ${user.toBase58()} (broker: ${broker.toBase58()}, basis points: ${brokerBasisPoints})\n      for token ${tokenMint.toBase58()} at price ${buyPrice}`);\n  let instruction = anchorProgram.instruction.createTradeState(\n    isBuy ? 1 : 0,\n    tradeBump,\n    buyPrice,\n    brokerBasisPoints,\n    tokenSize,\n    royalty_basis_points,\n    {\n      accounts: {\n        wallet: user,\n        collection: HYPERSPACE_ID,\n        brokerWallet: broker,\n        tokenAccount: tokenAccount,\n        tokenMint: tokenMint,\n        tradeState: tradeStateAddress,\n        systemProgram: SystemProgram.programId,\n        clock: SYSVAR_CLOCK_PUBKEY,\n      },\n    },\n  );\n  instruction.keys.filter(k => k.pubkey.equals(user)).map(k => (k.isSigner = true));\n  return instruction;\n}\n\nasync function helper(\n  anchorProgram: Program<Hyperspace>,\n  seller: PublicKey,\n  mintPublicKey: PublicKey,\n  sellerBrokerKey: PublicKey,\n  minAmountToReceive: BN,\n  // We don't charge for creating this\n  brokerBasisPoints: number = 0,\n  // This only matters when you cross\n  royaltyBasisPoints: number = 0,\n) {\n  const tokenSizeAdjusted = new BN(\n    await getPriceWithMantissa(1, mintPublicKey, seller, anchorProgram),\n  );\n\n  const tokenAccountKey = (await getAtaForMint(mintPublicKey, seller))[0];\n\n  // You should check that ATA exists\n  let instructions: TransactionInstruction[] = [];\n\n  const [programAsSigner, programAsSignerBump] = await getHyperspaceProgramAsSigner();\n\n  const [tradeState, tradeBump] = await getHyperspaceTradeState(\n    false,\n    seller,\n    tokenAccountKey,\n    mintPublicKey,\n    tokenSizeAdjusted,\n  );\n\n  const tradeStateAccount = await anchorProgram.provider.connection.getAccountInfo(\n    tradeState,\n    \"confirmed\",\n  );\n\n  if (!tradeStateAccount) {\n    const initTradeStateInstruction = await createTradeStateInstruction(\n      anchorProgram,\n      false,\n      tradeBump,\n      minAmountToReceive,\n      brokerBasisPoints,\n      royaltyBasisPoints,\n      tokenSizeAdjusted,\n      seller,\n      sellerBrokerKey,\n      tokenAccountKey,\n      mintPublicKey,\n      tradeState,\n    );\n    instructions.push(initTradeStateInstruction);\n  }\n\n  const tokenRecord = findTokenRecordPda(mintPublicKey, tokenAccountKey);\n\n  const editionAccount = (await getEditionDataAccount(mintPublicKey))[0];\n\n  const metadataAccount = await getMetadata(mintPublicKey);\n\n  const metadataObj = await anchorProgram.provider.connection.getAccountInfo(\n    metadataAccount,\n    \"confirmed\",\n  );\n\n  if (!metadataObj) {\n    throw Error(\"NFT does not have a metadata account, it may have been burnt.\");\n  }\n  const metadataParsed = Metadata.deserialize(metadataObj.data)[0];\n\n  const signers: Keypair[] = [];\n\n  let marketplaceObj = await anchorProgram.account.hyperspace.fetch(\n    HYPERSPACE_MARKETPLACE_INSTANCE,\n  );\n\n  const sellInstruction = await anchorProgram.instruction.sell(\n    tradeBump,\n    programAsSignerBump,\n    minAmountToReceive,\n    brokerBasisPoints,\n    tokenSizeAdjusted,\n    royaltyBasisPoints,\n    {\n      accounts: {\n        wallet: seller,\n        sellerBrokerWallet: sellerBrokerKey,\n        tokenMint: mintPublicKey,\n        tokenAccount: tokenAccountKey,\n        metadata: metadataAccount,\n        authority: marketplaceObj.authority,\n        hyperspace: HYPERSPACE_MARKETPLACE_INSTANCE,\n        hyperspaceFeeAccount: marketplaceObj.hyperspaceFeeAccount,\n        sellerTradeState: tradeState,\n        tokenProgram: TOKEN_PROGRAM_ID,\n        systemProgram: SystemProgram.programId,\n        metadataProgram: TOKEN_METADATA_PROGRAM_ID,\n        programAsSigner: programAsSigner,\n        instructions: SYSVAR_INSTRUCTIONS_PUBKEY,\n        tokenRecord: tokenRecord,\n        editionAccount: editionAccount,\n        authorizationRules: metadataParsed.programmableConfig?.ruleSet\n          ? metadataParsed.programmableConfig.ruleSet\n          : TOKEN_METADATA_PROGRAM_ID,\n        mplTokenAuthRulesProgram: AUTH_PROGRAM_ID,\n        clock: SYSVAR_CLOCK_PUBKEY,\n      },\n      signers,\n    },\n  );\n  instructions.push(sellInstruction);\n  return instructions;\n}\n\nasync function hyperspaceCreateListTx(seller: string, token: string, price: number) {\n  let provider = new AnchorProvider(CONNECTION, new NodeWallet(Keypair.generate()), {});\n  let program = new Program(hyperspaceIdl, HYPERSPACE_ID, provider);\n  const sellerKey = new PublicKey(seller);\n\n  const tokenKey = new PublicKey(token);\n  let mint: PublicKey;\n  try {\n    const tokenAI = await getAccount(CONNECTION, tokenKey);\n    console.log(\"found tokenAI:\", tokenAI);\n    mint = tokenAI.mint;\n  } catch (e) {\n    mint = tokenKey;\n  }\n\n  let instructions = await helper(\n    program,\n    sellerKey,\n    mint,\n    SystemProgram.programId,\n    new BN(price * LAMPORTS_PER_SOL),\n  );\n\n  let tx = new Transaction();\n  for (const ix of instructions) {\n    tx = tx.add(ix);\n  }\n  tx.recentBlockhash = (await CONNECTION.getLatestBlockhash()).blockhash;\n  tx.feePayer = sellerKey;\n\n  const txBytes = tx.serialize({ requireAllSignatures: false }).toString(\"base64\");\n\n  return {\n    transaction: txBytes,\n  };\n}\n\nexport async function createListNFT(req: NextApiRequest) {\n  const { token, price } = req.query;\n  const { account: seller } = req.body;\n  console.log(seller, token, price);\n  return await hyperspaceCreateListTx(\n    seller as string,\n    token as string,\n    Number.parseFloat(price as string),\n  );\n}\n\nexport default makeRespondToSolanaPayGet(makeRespondToSolanaPayPost(createListNFT));\n"
  },
  {
    "path": "chatgpt-plugin/src/pages/api/handlers/solana-pay/sign/createMerkleTree.ts",
    "content": "// DEPRECATED - not in use\nimport {\n  PROGRAM_ID as BUBBLEGUM_PROGRAM_ID,\n  createCreateTreeInstruction,\n} from \"@metaplex-foundation/mpl-bubblegum\";\nimport {\n  SPL_ACCOUNT_COMPRESSION_PROGRAM_ID,\n  SPL_NOOP_PROGRAM_ID,\n  createAllocTreeIx,\n} from \"@solana/spl-account-compression\";\nimport { Keypair, PublicKey, Transaction } from \"@solana/web3.js\";\nimport dotenv from \"dotenv\";\nimport { NextApiRequest } from \"next\";\nimport { CONNECTION } from \"../../../constants\";\ndotenv.config();\n\n//TODO(@ngundotra): have to add closeTree instruction before exposing this endpoint\n\nexport interface MerkleTreeArgs {\n  maxDepth: number;\n  maxBufferSize: number;\n  canopyHeight: number;\n}\n\nexport async function createTree(req: NextApiRequest) {\n  const { account: payerPublicKey, canopyDepth, maxDepthSizePair } = req.body;\n  const treeKeypair = Keypair.generate();\n\n  const [treeAuthority, _bump] = PublicKey.findProgramAddressSync(\n    [treeKeypair.publicKey.toBuffer()],\n    BUBBLEGUM_PROGRAM_ID,\n  );\n\n  // instruction for space allocation for tree account\n  const allocTreeIx = await createAllocTreeIx(\n    CONNECTION,\n    treeKeypair.publicKey,\n    payerPublicKey,\n    maxDepthSizePair,\n    canopyDepth,\n  );\n\n  // instruction for tree creation\n  const createTreeIx = createCreateTreeInstruction(\n    {\n      payer: payerPublicKey,\n      treeCreator: payerPublicKey,\n      treeAuthority: treeAuthority,\n      merkleTree: treeKeypair.publicKey,\n      compressionProgram: SPL_ACCOUNT_COMPRESSION_PROGRAM_ID,\n      logWrapper: SPL_NOOP_PROGRAM_ID,\n    },\n    {\n      maxBufferSize: maxDepthSizePair.maxBufferSize,\n      maxDepth: maxDepthSizePair.maxDepth,\n      public: false,\n    },\n    BUBBLEGUM_PROGRAM_ID,\n  );\n\n  try {\n    const tx = new Transaction();\n    tx.add(allocTreeIx);\n    tx.add(createTreeIx);\n    tx.feePayer = payerPublicKey;\n    return tx;\n  } catch (e) {\n    console.error(e);\n    throw e;\n  }\n}\n"
  },
  {
    "path": "chatgpt-plugin/src/pages/api/handlers/solana-pay/sign/createMintCNFT.ts",
    "content": "// DEPRECATED - not in use\nimport { JsonMetadata, Metaplex } from \"@metaplex-foundation/js\";\nimport {\n  PROGRAM_ID as BUBBLEGUM_PROGRAM_ID,\n  MetadataArgs,\n  TokenProgramVersion,\n  TokenStandard,\n  createMintToCollectionV1Instruction,\n} from \"@metaplex-foundation/mpl-bubblegum\";\nimport { PROGRAM_ID as TOKEN_METADATA_PROGRAM_ID } from \"@metaplex-foundation/mpl-token-metadata\";\nimport {\n  SPL_ACCOUNT_COMPRESSION_PROGRAM_ID,\n  SPL_NOOP_PROGRAM_ID,\n} from \"@solana/spl-account-compression\";\nimport { Keypair, PublicKey, Transaction } from \"@solana/web3.js\";\nimport dotenv from \"dotenv\";\nimport { NextApiRequest } from \"next\";\nimport { makeRespondToSolanaPayGet, makeRespondToSolanaPayPost } from \".\";\nimport configConstants, { CONNECTION } from \"../../../constants\";\nconfigConstants();\ndotenv.config();\n\nasync function createMintCNFT(req: NextApiRequest) {\n  const { metadataUri } = req.query;\n  const { account: payer } = req.body;\n\n  const metaplex = Metaplex.make(CONNECTION);\n  // create compressed nft\n  const nftMetadata = await metaplex.storage().downloadJson<JsonMetadata>(metadataUri as string);\n  const compressedNftMetadata: MetadataArgs = {\n    name: nftMetadata.name ?? \"\",\n    symbol: nftMetadata.symbol ?? \"\",\n    uri: metadataUri as string,\n    sellerFeeBasisPoints: 0,\n    creators: [\n      {\n        address: new PublicKey(payer),\n        verified: true,\n        share: 100,\n      },\n    ],\n    editionNonce: 0,\n    uses: null,\n    collection: null,\n    isMutable: false,\n    primarySaleHappened: false,\n    tokenProgramVersion: TokenProgramVersion.Original,\n    tokenStandard: TokenStandard.NonFungible,\n  };\n\n  // derive PDA (owned bt Bubblegum) to act as the signer of the compressed minting\n  const [bubblegumSigner, _bump] = PublicKey.findProgramAddressSync(\n    [Buffer.from(\"collection_cpi\", \"utf8\")],\n    BUBBLEGUM_PROGRAM_ID,\n  );\n  const collectionOwner = Keypair.fromSecretKey(\n    Uint8Array.from(Buffer.from(process.env.COLLECTION_OWNER_SECRET_KEY as string, \"base64\")),\n  );\n  const mintToCollectionIx = createMintToCollectionV1Instruction(\n    {\n      payer: new PublicKey(payer),\n\n      merkleTree: new PublicKey(process.env.TREE_ADDRESS_1 as string),\n      treeAuthority: new PublicKey(process.env.TREE_AUTHORITY_1 as string),\n      treeDelegate: new PublicKey(payer),\n\n      collectionMint: new PublicKey(process.env.CHATGPT_COLLECTION_MINT as string),\n      collectionAuthority: collectionOwner.publicKey,\n      collectionMetadata: new PublicKey(process.env.CHATGPT_COLLECTION_METADATA_ACCOUNT as string),\n      collectionAuthorityRecordPda: BUBBLEGUM_PROGRAM_ID,\n      editionAccount: new PublicKey(\n        process.env.CHATGPT_COLLECTION_MASTER_EDITION_ACCOUNT as string,\n      ),\n\n      leafOwner: new PublicKey(payer),\n      leafDelegate: new PublicKey(payer),\n\n      // other accounts\n      compressionProgram: SPL_ACCOUNT_COMPRESSION_PROGRAM_ID,\n      logWrapper: SPL_NOOP_PROGRAM_ID,\n      bubblegumSigner: bubblegumSigner,\n      tokenMetadataProgram: TOKEN_METADATA_PROGRAM_ID,\n    },\n    {\n      metadataArgs: Object.assign(compressedNftMetadata, {\n        collection: {\n          key: new PublicKey(process.env.CHATGPT_COLLECTION_MINT as string),\n          verified: false,\n        },\n      }),\n    },\n  );\n\n  const tx = new Transaction();\n  tx.add(mintToCollectionIx);\n  tx.feePayer = new PublicKey(payer);\n  tx.recentBlockhash = (await CONNECTION.getLatestBlockhash()).blockhash;\n  tx.partialSign(collectionOwner);\n  return {\n    transaction: tx.serialize({ requireAllSignatures: false }).toString(\"base64\"),\n  };\n}\nexport default makeRespondToSolanaPayGet(makeRespondToSolanaPayPost(createMintCNFT));\n"
  },
  {
    "path": "chatgpt-plugin/src/pages/api/handlers/solana-pay/sign/createMintNFT.ts",
    "content": "// DEPRECATED - not in use\nimport { Metaplex } from \"@metaplex-foundation/js\";\nimport { PublicKey } from \"@solana/web3.js\";\nimport { NextApiRequest } from \"next\";\nimport { makeRespondToSolanaPayGet, makeRespondToSolanaPayPost } from \".\";\nimport configConstants, { CONNECTION } from \"../../../constants\";\nconfigConstants();\n\nasync function createMintNFT(req: NextApiRequest) {\n  const { name, metadataUri, sellerFee = 0 } = req.query;\n  const { account: sender } = req.body;\n  const metaplex = Metaplex.make(CONNECTION);\n  const mintTransactionBuilder = await metaplex\n    .nfts()\n    .builders()\n    .create({\n      name: name as string,\n      uri: metadataUri as string,\n      sellerFeeBasisPoints: Number(sellerFee),\n    });\n  const latestBlockhashWithExpiryBlockHeight = await CONNECTION.getLatestBlockhash();\n  const tx = mintTransactionBuilder.toTransaction(latestBlockhashWithExpiryBlockHeight);\n  tx.feePayer = new PublicKey(sender);\n  return {\n    transaction: tx.serialize({ requireAllSignatures: false }).toString(\"base64\"),\n  };\n}\nexport default makeRespondToSolanaPayGet(makeRespondToSolanaPayPost(createMintNFT));\n"
  },
  {
    "path": "chatgpt-plugin/src/pages/api/handlers/solana-pay/sign/createSetProfilePic.ts",
    "content": "// DEPRECATED - not in use\nimport { NextApiRequest } from \"next\";\nimport { PublicKey } from \"@solana/web3.js\";\n\nimport { makeRespondToSolanaPayGet, makeRespondToSolanaPayPost } from \".\";\nimport configConstants, { CONNECTION } from \"../../../constants\";\nimport { createSetProfilePictureTransaction } from \"@solflare-wallet/pfp\";\nconfigConstants();\n\nasync function createSetProfilePic(req: NextApiRequest) {\n  const { mintPublicKey, tokenAccountPublicKey } = req.query;\n  const { account: ownerAccountPublicKey } = req.body;\n\n  const tx = await createSetProfilePictureTransaction(\n    new PublicKey(ownerAccountPublicKey),\n    new PublicKey(mintPublicKey as string),\n    new PublicKey(tokenAccountPublicKey as string),\n  );\n  tx.feePayer = new PublicKey(ownerAccountPublicKey);\n  tx.recentBlockhash = (await CONNECTION.getLatestBlockhash()).blockhash;\n\n  return {\n    transaction: tx.serialize({ requireAllSignatures: false }).toString(\"base64\"),\n  };\n}\n\nexport default makeRespondToSolanaPayGet(makeRespondToSolanaPayPost(createSetProfilePic));\n"
  },
  {
    "path": "chatgpt-plugin/src/pages/api/handlers/solana-pay/sign/createTransferAsset.ts",
    "content": "// DEPRECATED - not in use\nimport {\n  SPL_ACCOUNT_COMPRESSION_PROGRAM_ID,\n  SPL_NOOP_PROGRAM_ID,\n} from \"@solana/spl-account-compression\";\nimport { PublicKey, Transaction } from \"@solana/web3.js\";\nimport { NextApiRequest } from \"next\";\n\nimport { bs58 } from \"@coral-xyz/anchor/dist/cjs/utils/bytes\";\nimport { createTransferInstruction } from \"@metaplex-foundation/mpl-bubblegum\";\nimport { makeRespondToSolanaPayGet, makeRespondToSolanaPayPost } from \".\";\nimport configConstants, { CONNECTION } from \"../../../constants\";\nimport { bufferToArray, getAsset, getAssetProof, getBubblegumAuthorityPDA } from \"../utils/helpers\";\nconfigConstants();\n\nasync function createTransferAsset(req: NextApiRequest) {\n  const { destination, assetId } = req.query;\n  const { account: sender } = req.body;\n  if (!sender || !destination || !assetId) {\n    throw new Error(\"Missing required parameters\");\n  }\n\n  const assetProof = await getAssetProof(assetId, CONNECTION.rpcEndpoint);\n  if (!assetProof?.proof || assetProof.proof.length === 0) {\n    throw new Error(\"Proof retrieved for the given assetId is empty. Please check the assetId.\");\n  }\n  const proofPath = assetProof.proof.map((node: string) => ({\n    pubkey: new PublicKey(node),\n    isSigner: false,\n    isWritable: false,\n  }));\n\n  const rpcAsset = await getAsset(assetId, CONNECTION.rpcEndpoint);\n  if (rpcAsset.ownership.owner !== sender) {\n    throw new Error(\n      `NFT is not owned by the expected owner. Expected ${new PublicKey(sender)} but got ${\n        rpcAsset.ownership.owner\n      }.`,\n    );\n  }\n\n  const leafNonce = rpcAsset.compression.leaf_id;\n  const treeAuthority = await getBubblegumAuthorityPDA(new PublicKey(assetProof.tree_id));\n  const leafDelegate = rpcAsset.ownership.delegate\n    ? new PublicKey(rpcAsset.ownership.delegate)\n    : new PublicKey(rpcAsset.ownership.owner);\n  let transferIx = createTransferInstruction(\n    {\n      treeAuthority,\n      leafOwner: new PublicKey(rpcAsset.ownership.owner),\n      leafDelegate: leafDelegate,\n      newLeafOwner: new PublicKey(destination as string),\n      merkleTree: new PublicKey(assetProof.tree_id),\n      logWrapper: SPL_NOOP_PROGRAM_ID,\n      compressionProgram: SPL_ACCOUNT_COMPRESSION_PROGRAM_ID,\n      anchorRemainingAccounts: proofPath,\n    },\n    {\n      root: bufferToArray(bs58.decode(assetProof.root)),\n      dataHash: bufferToArray(bs58.decode(rpcAsset.compression.data_hash.trim())),\n      creatorHash: bufferToArray(bs58.decode(rpcAsset.compression.creator_hash.trim())),\n      nonce: leafNonce,\n      index: leafNonce,\n    },\n  );\n  const tx = new Transaction().add(transferIx);\n  tx.feePayer = new PublicKey(sender);\n  tx.recentBlockhash = (await CONNECTION.getLatestBlockhash()).blockhash;\n  return {\n    transaction: tx.serialize({ requireAllSignatures: false }).toString(\"base64\"),\n  };\n}\nexport default makeRespondToSolanaPayGet(makeRespondToSolanaPayPost(createTransferAsset));\n"
  },
  {
    "path": "chatgpt-plugin/src/pages/api/handlers/solana-pay/sign/createTransferSol.ts",
    "content": "import { NextApiRequest } from \"next\";\nimport { PublicKey, Transaction, SystemProgram, LAMPORTS_PER_SOL } from \"@solana/web3.js\";\n\nimport { makeRespondToSolanaPayGet, makeRespondToSolanaPayPost } from \".\";\nimport configConstants, { CONNECTION } from \"../../../constants\";\nconfigConstants();\n\nasync function createTransferSol(req: NextApiRequest) {\n  const { amount } = req.query;\n  const { account } = req.body;\n\n  const sender = new PublicKey(account);\n  const destination = req.query[\"destination\"] as any as PublicKey;\n\n  const tx = new Transaction();\n  tx.add(\n    SystemProgram.transfer({\n      fromPubkey: sender,\n      toPubkey: destination,\n      lamports: Math.floor(parseFloat(amount as string) * LAMPORTS_PER_SOL),\n    }),\n  );\n  tx.feePayer = sender;\n  tx.recentBlockhash = (await CONNECTION.getLatestBlockhash()).blockhash;\n\n  return {\n    transaction: tx.serialize({ requireAllSignatures: false }).toString(\"base64\"),\n  };\n}\n\nexport default makeRespondToSolanaPayGet(\n  makeRespondToSolanaPayPost(createTransferSol, { addresses: [\"destination\"] }),\n);\n"
  },
  {
    "path": "chatgpt-plugin/src/pages/api/handlers/solana-pay/sign/createTransferToken.ts",
    "content": "import { NextApiRequest } from \"next\";\nimport { PublicKey, Transaction } from \"@solana/web3.js\";\nimport {\n  getAssociatedTokenAddressSync,\n  createTransferCheckedInstruction,\n  getMint,\n  Mint,\n} from \"@solana/spl-token\";\nimport { makeRespondToSolanaPayGet, makeRespondToSolanaPayPost } from \".\";\nimport configConstants, { CONNECTION } from \"../../../constants\";\nconfigConstants();\n\nasync function createTransferToken(req: NextApiRequest) {\n  const { amount } = req.query;\n  const { account } = req.body;\n\n  const sender = new PublicKey(account);\n  const destination = req.query[\"destination\"] as any as PublicKey;\n  const mint = req.query[\"mint\"] as any as PublicKey;\n\n  let mintAccount: Mint;\n  try {\n    mintAccount = await getMint(CONNECTION, mint, \"confirmed\");\n  } catch (error) {\n    throw new Error(`Mint ${mint.toString()} not found`);\n  }\n\n  const sourceToken = getAssociatedTokenAddressSync(mint, sender);\n  const destinationToken = getAssociatedTokenAddressSync(mint, destination);\n\n  const tx = new Transaction();\n  const tokens = Math.round(Number(amount as string) * Math.pow(10, mintAccount.decimals));\n\n  tx.add(\n    createTransferCheckedInstruction(\n      sourceToken,\n      mint,\n      destinationToken,\n      sender,\n      tokens,\n      mintAccount.decimals,\n    ),\n  );\n  tx.feePayer = sender;\n  tx.recentBlockhash = (await CONNECTION.getLatestBlockhash()).blockhash;\n\n  return {\n    transaction: tx.serialize({ requireAllSignatures: false }).toString(\"base64\"),\n  };\n}\n\nexport default makeRespondToSolanaPayGet(\n  makeRespondToSolanaPayPost(createTransferToken, { addresses: [\"destination\"], tokens: [\"mint\"] }),\n);\n"
  },
  {
    "path": "chatgpt-plugin/src/pages/api/handlers/solana-pay/sign/createWriteNFTMetadata.ts",
    "content": "// DEPRECATED - not in use\nimport { createWriteNFTMetadataTx } from \"@/lib/on-chain-metadata\";\nimport { NextApiRequest } from \"next\";\nimport { makeRespondToSolanaPayGet, makeRespondToSolanaPayPost } from \".\";\nimport configConstants, { CONNECTION } from \"../../../constants\";\nconfigConstants();\n\nasync function createWriteNFTMetadata(req: NextApiRequest) {\n  const { image } = req.query;\n  const { account: owner } = req.body;\n  return await createWriteNFTMetadataTx(CONNECTION, owner as string, {\n    image,\n  });\n}\n\nexport default makeRespondToSolanaPayGet(makeRespondToSolanaPayPost(createWriteNFTMetadata));\n"
  },
  {
    "path": "chatgpt-plugin/src/pages/api/handlers/solana-pay/sign/index.ts",
    "content": "import { NextApiHandler, NextApiRequest, NextApiResponse } from \"next\";\nimport { SOLANA_PAY_LABEL } from \"../../../constants\";\nimport { Options, makeApiPostRequest } from \"@/lib/middleware\";\nexport type TransactionHandler = (req: NextApiRequest) => Promise<{ transaction: string }>;\n\nexport function makeRespondToSolanaPayGet(apiHandler: NextApiHandler) {\n  return async (req: NextApiRequest, res: NextApiResponse) => {\n    if (req.method === \"GET\") {\n      res.status(200).json({\n        label: SOLANA_PAY_LABEL,\n        icon: \"https://solanapay.com/src/img/branding/Solanapay.com/downloads/gradient.svg\",\n      });\n    } else {\n      await apiHandler(req, res);\n    }\n  };\n}\n\nexport function makeRespondToSolanaPayPost(handler: TransactionHandler, options?: Options) {\n  return makeApiPostRequest(\n    async (req: NextApiRequest, res: NextApiResponse) => {\n      let result = await handler(req);\n      res.status(200).json(result);\n    },\n    { ...options, rewriteQuery: true },\n  );\n}\n"
  },
  {
    "path": "chatgpt-plugin/src/pages/api/handlers/solana-pay/utils/helpers.ts",
    "content": "import { BN } from \"@coral-xyz/anchor\";\nimport { PROGRAM_ID, TreeConfig } from \"@metaplex-foundation/mpl-bubblegum\";\nimport { PROGRAM_ID as TOKEN_METADATA_PROGRAM_ID } from \"@metaplex-foundation/mpl-token-metadata\";\nimport { Connection, PublicKey } from \"@solana/web3.js\";\nimport axios from \"axios\";\n\nexport async function getBubblegumAuthorityPDA(merkleRollPubKey: PublicKey) {\n  const [bubblegumAuthorityPDAKey] = await PublicKey.findProgramAddress(\n    [merkleRollPubKey.toBuffer()],\n    PROGRAM_ID,\n  );\n  return bubblegumAuthorityPDAKey;\n}\n\nexport async function getNonceCount(connection: Connection, tree: PublicKey): Promise<BN> {\n  const treeAuthority = await getBubblegumAuthorityPDA(tree);\n  return new BN((await TreeConfig.fromAccountAddress(connection, treeAuthority)).numMinted);\n}\n\nexport function bufferToArray(buffer: Buffer): number[] {\n  const nums = [];\n  for (let i = 0; i < buffer.length; i++) {\n    nums.push(buffer[i]);\n  }\n  return nums;\n}\n\nexport async function getVoucherPDA(tree: PublicKey, leafIndex: number): Promise<PublicKey> {\n  const [voucher] = await PublicKey.findProgramAddress(\n    [\n      Buffer.from(\"voucher\", \"utf8\"),\n      tree.toBuffer(),\n      Uint8Array.from(new BN(leafIndex).toArray(\"le\", 8)),\n    ],\n    PROGRAM_ID,\n  );\n  return voucher;\n}\n\nexport async function getMetadata(mint: PublicKey) {\n  return (\n    await PublicKey.findProgramAddress(\n      [Buffer.from(\"metadata\"), TOKEN_METADATA_PROGRAM_ID.toBuffer(), mint.toBuffer()],\n      TOKEN_METADATA_PROGRAM_ID,\n    )\n  )[0];\n}\n\nexport async function getMasterEdition(mint: PublicKey) {\n  return (\n    await PublicKey.findProgramAddress(\n      [\n        Buffer.from(\"metadata\"),\n        TOKEN_METADATA_PROGRAM_ID.toBuffer(),\n        mint.toBuffer(),\n        Buffer.from(\"edition\"),\n      ],\n      TOKEN_METADATA_PROGRAM_ID,\n    )\n  )[0];\n}\n\nexport async function getAsset(assetId: any, rpcUrl: any): Promise<any> {\n  try {\n    const response = await axios.post(rpcUrl, {\n      jsonrpc: \"2.0\",\n      method: \"get_asset\",\n      id: \"compression-example\",\n      params: [assetId],\n    });\n    return response.data.result;\n  } catch (error) {\n    console.error(error);\n  }\n}\n\nexport async function getAssetProof(assetId: any, rpcUrl: any): Promise<any> {\n  try {\n    const response = await axios.post(rpcUrl, {\n      jsonrpc: \"2.0\",\n      method: \"get_asset_proof\",\n      id: \"compression-example\",\n      params: [assetId],\n    });\n    return response.data.result;\n  } catch (error) {\n    console.error(error);\n  }\n}\n"
  },
  {
    "path": "chatgpt-plugin/src/pages/api/handlers/tokenName/index.ts",
    "content": "import { NextApiRequest, NextApiResponse } from \"next\";\nimport { Client } from \"@solflare-wallet/utl-sdk\";\nimport { makeApiPostRequest } from \"@/lib/middleware\";\nconst utl = new Client();\n\nasync function handler(req: NextApiRequest, res: NextApiResponse) {\n  const { tokenName } = req.body;\n\n  let results = await (\n    await utl.searchMints(tokenName)\n  )\n    .filter((res: any) => {\n      return res[\"verified\"] && res[\"holders\"] > 0 && res[\"chainId\"] === 101;\n    })\n    .map((res: any) => {\n      return {\n        mintAddress: res[\"address\"],\n        tokenName: res[\"name\"],\n        tokenSymbol: res[\"symbol\"],\n        holders: res[\"holders\"],\n        verified: res[\"verified\"],\n        chainId: res[\"chainId\"],\n        logoURI: res[\"logoURI\"],\n      };\n    });\n\n  res.status(200).json(results.slice(0, 10));\n}\nexport default makeApiPostRequest(handler);\n"
  },
  {
    "path": "chatgpt-plugin/src/pages/api/handlers/tx-link/[txSlug].ts",
    "content": "import { NextApiRequest, NextApiResponse } from \"next\";\nimport { encode } from \"querystring\";\nimport configConstants, { SELF_URL } from \"../../constants\";\nconfigConstants();\nimport { makeApiPostRequest } from \"@/lib/middleware\";\n\nasync function handler(req: NextApiRequest, res: NextApiResponse) {\n  const {\n    query: { txSlug },\n  } = req;\n\n  let encoded = encode(Object(req.body));\n  res.status(200).send({\n    qrCode: `${SELF_URL}/api/handlers/solana-pay/qr/${txSlug}?${encoded}`,\n    disclaimer: `This product uses artificial intelligence (\"AI\"), which may produce inaccurate information. You are responsible for transactions you authorize, so please confirm accuracy of instructions prior to authorizing any transaction.`,\n  });\n}\n\nexport default makeApiPostRequest(handler);\n"
  },
  {
    "path": "chatgpt-plugin/src/pages/api/handlers/walletName/index.ts",
    "content": "import { NextApiRequest, NextApiResponse } from \"next\";\nimport { PublicKey } from \"@solana/web3.js\";\nimport { walletNameToAddressAndProfilePicture } from \"@portal-payments/solana-wallet-names\";\nimport { walletAddressToNameAndProfilePictureCustom } from \"@/lib/address\";\nimport configConstants, { CONNECTION } from \"../../constants\";\nconfigConstants();\nimport { makeApiPostRequest } from \"@/lib/middleware\";\n\nasync function handler(req: NextApiRequest, res: NextApiResponse) {\n  const { walletName } = req.body;\n  try {\n    const dotAnything = await walletAddressToNameAndProfilePictureCustom(\n      CONNECTION,\n      new PublicKey(walletName),\n    );\n    res.status(200).send(dotAnything);\n  } catch (error) {\n    const walletInfo = await walletNameToAddressAndProfilePicture(CONNECTION, walletName);\n    res.status(200).send(walletInfo);\n  }\n}\n\nexport default makeApiPostRequest(handler);\n"
  },
  {
    "path": "chatgpt-plugin/src/pages/api/helloMoon/defi/defi.yaml",
    "content": "openapi: 3.1.0\ninfo:\n  title: HelloMoon Defi Summary API\n  description: An API for querying information about defi protocols on Solana\n  version: 1.0.0\nservers:\n  - url: https://chatgpt.solanalabs.com/api/helloMoon\npaths:\n  /defi/programNewUsers:\n    post:\n      summary: Shows new users of a program over time.\n      description:\n        Shows new users of a program over time. A new user is defined as the first time a publicKey\n        has been a fee payer for this program address.\n      operationId: query_program_new_users\n      requestBody:\n        content:\n          application/json:\n            schema:\n              $ref: \"#/components/schemas/programNewUsersRequest\"\n      responses:\n        \"200\":\n          description: Successful Response\n  /defi/programOverlap:\n    post:\n      summary: Shows overlap between users of two different programs\n      description:\n      Program overlap shows the overlap between the users of two protocols. The data will show what percentage of 'aProgramIds' users also use 'bProgramId'\n      operationId: query_program_user_overlap\n      requestBody:\n        content:\n          application/json:\n            schema:\n              $ref: \"#/components/schemas/programOverlapRequest\"\n      responses:\n        \"200\":\n          description: Successful Response\n  /defi/tokenUsers:\n    post:\n      summary:\n      description:\n      operationId: query_token_users\n      requestBody:\n        content:\n          application/json:\n            schema:\n              $ref: \"#/components/schemas/tokenUsersRequest\"\n      responses:\n        \"200\":\n          description: Successful Response\n  /defi/tokenStats:\n    post:\n      summary:\n      description:\n      operationId: query_token_stats\n      requestBody:\n        content:\n          application/json:\n            schema:\n              $ref: \"#/components/schemas/tokenStatsRequest\"\n      responses:\n        \"200\":\n          description: Successful Response\ncomponents:\n  schemas:\n    stringComparisonOperator:\n      type: string\n      enum:\n        - \"=\"\n        - \"!=\"\n        - \">\"\n        - \"<\"\n        - \">=\"\n      nullable: true\n    nullableNumber:\n      type: number\n      nullable: true\n    programNewUsersRequest:\n      type: object\n      required:\n        - programId\n      properties:\n        programId:\n          type: string\n        day:\n          type: string\n          nullable: true\n        limit:\n          $ref: \"#/components/schemas/nullableNumber\"\n    programOverlapRequest:\n      type: object\n      properties:\n        aProgramId:\n          type: string\n        bProgramId:\n          type: string\n        limit:\n          $ref: \"#/components/schemas/nullableNumber\"\n    tokenUsersRequest:\n      type: object\n      properties:\n        day:\n          type: string\n          nullable: true\n        mint:\n          type: string\n        limit:\n          $ref: \"#/components/schemas/nullableNumber\"\n    tokenStatsRequest:\n      type: object\n      properties:\n        granularity:\n          type: string\n          enum:\n            - \"one_week\"\n            - \"one_month\"\n            - \"one_day\"\n            - \"six_hour\"\n            - \"one_hour\"\n            - \"thirty_min\"\n          nullable: true\n        mint:\n          type: string\n          nullable: true\n        limit:\n          $ref: \"#/components/schemas/nullableNumber\"\n"
  },
  {
    "path": "chatgpt-plugin/src/pages/api/helloMoon/defi/programNewUsers.ts",
    "content": "// Deprecated - Not in use\nimport { NextApiRequest, NextApiResponse } from \"next\";\nimport configConstants, { HELLOMOON_CLIENT } from \"../../constants\";\nconfigConstants();\n\nimport { DefiProgramNetNewUsersDailyRequest } from \"@hellomoon/api\";\n\nexport default async function handler(req: NextApiRequest, res: NextApiResponse) {\n  const { programId, limit, day } = req.body;\n\n  let date: Date = new Date();\n  if (day) {\n    try {\n      date = new Date(day);\n    } catch (e) {\n      res.status(500).send(\"Invalid day: \" + day);\n      return;\n    }\n  }\n\n  let args = new DefiProgramNetNewUsersDailyRequest({\n    programId,\n    limit: limit ?? 10,\n    day: date ? date.toISOString() : undefined,\n  });\n\n  let data = await HELLOMOON_CLIENT.send(args)\n    .then(result => result.data)\n    .catch(console.error);\n\n  res.status(200).send(data);\n}\n"
  },
  {
    "path": "chatgpt-plugin/src/pages/api/helloMoon/defi/programOverlap.ts",
    "content": "// Deprecated - Not in use\nimport { NextApiRequest, NextApiResponse } from \"next\";\nimport configConstants, { HELLOMOON_CLIENT } from \"../../constants\";\n\nimport { DefiProgramOverlapRequest } from \"@hellomoon/api\";\nconfigConstants();\n\nexport default async function handler(req: NextApiRequest, res: NextApiResponse) {\n  const { aProgramId, bProgramId, limit } = req.body;\n\n  let args = new DefiProgramOverlapRequest({\n    aProgramId,\n    bProgramId,\n    limit: limit ?? 10,\n  });\n\n  let data = await HELLOMOON_CLIENT.send(args)\n    .then(result => result.data)\n    .catch(console.error);\n\n  res.status(200).send(data);\n}\n"
  },
  {
    "path": "chatgpt-plugin/src/pages/api/helloMoon/defi/tokenStats.ts",
    "content": "import { NextApiRequest, NextApiResponse } from \"next\";\nimport configConstants, { HELLOMOON_CLIENT } from \"../../constants\";\nconfigConstants();\n\nimport { DefiTokenLeaderboardV3Request } from \"@hellomoon/api\";\nimport { makeApiPostRequest } from \"@/lib/middleware\";\n\nconst GRANULARITY = [\"one_day\", \"one_week\", \"one_month\", \"thirty_min\", \"one_hour\", \"six_hour\"];\nasync function handler(req: NextApiRequest, res: NextApiResponse) {\n  const { granularity, limit } = req.body;\n\n  if (granularity && !GRANULARITY.includes(granularity.toLowerCase())) {\n    res.status(500).send(\"Invalid granularity: \" + granularity);\n    return;\n  }\n\n  let args = new DefiTokenLeaderboardV3Request({\n    granularity: granularity ? ((granularity as string).toLocaleUpperCase() as any) : \"ONE_DAY\",\n    mint: req.body[\"mint\"].toString(),\n    limit: limit ?? 10,\n  });\n\n  let data = await HELLOMOON_CLIENT.send(args)\n    .then(result => result.data)\n    .catch(console.error);\n\n  res.status(200).send(data);\n}\n\nexport default makeApiPostRequest(handler, { tokens: [\"mint\"] });\n"
  },
  {
    "path": "chatgpt-plugin/src/pages/api/helloMoon/defi/tokenUsers.ts",
    "content": "import { NextApiRequest, NextApiResponse } from \"next\";\nimport configConstants, { HELLOMOON_CLIENT } from \"../../constants\";\nconfigConstants();\n\nimport { DefiTokenUsersDailyRequest } from \"@hellomoon/api\";\nimport { makeApiPostRequest } from \"@/lib/middleware\";\n\nasync function handler(req: NextApiRequest, res: NextApiResponse) {\n  const { day, limit } = req.body;\n\n  let args = new DefiTokenUsersDailyRequest({\n    mint: req.body[\"mint\"].toString(),\n    day: day ? new Date(day).toISOString() : undefined,\n    limit: limit ?? 10,\n  });\n\n  let data = await HELLOMOON_CLIENT.send(args)\n    .then(result => result.data)\n    .catch(console.error);\n\n  res.status(200).send(data);\n}\nexport default makeApiPostRequest(handler, { tokens: [\"mint\"] });\n"
  },
  {
    "path": "chatgpt-plugin/src/pages/api/helloMoon/jupiter/README.md",
    "content": "# Test Queries\n\n#### Summary \n\nCan you summarize Jupiter activity this past week on Solana?\n\n```\nIn the past week, Jupiter on Solana has seen a total trading volume of approximately $11,179,154.79 USD. There have been 29,968 unique users and 686,025 transactions during this period. Overall, Jupiter has been quite active on Solana. Is there anything else you would like to know?\n```\n\n#### Historical\n\nCan you just print out the last 7 days of jupiter volume in USD?\n\n```\nHere are the last 7 days of Jupiter volume in USD:\n\nJuly 20, 2023: $7,557,612.35 USD\nJuly 19, 2023: $10,989,070.90 USD\nJuly 18, 2023: $12,249,619.75 USD\nJuly 17, 2023: $16,730,144.59 USD\nJuly 16, 2023: $10,583,573.39 USD\nJuly 15, 2023: $17,052,417.78 USD\nJuly 14, 2023: $36,812,293.04 USD\nPlease note that these values represent the daily trading volume on each respective day. Is there anything else you would like to know?\n```\n\n#### Pairs\n\nWhat was mSOL - wSOL pair volume this week on Jupiter?\n\n```\nThe mSOL - wSOL pair volume this week on Jupiter was approximately $229,340.42.\n```\n\n\n#### Swaps\n\nWhat was the whole market volume of wSOL to mSOL swaps done on Solana this week?\n```\nThe total market volume of wSOL to mSOL swaps done on Solana this week was $16,499,552.12.\n```\n\nWhat about the volume of mSOL - wSOL swaps just on Jupiter this week?\n```\nThe volume of mSOL - wSOL swaps on Jupiter this week was $2,801,324.60.\n```\n"
  },
  {
    "path": "chatgpt-plugin/src/pages/api/helloMoon/jupiter/historical.ts",
    "content": "import { NextApiRequest, NextApiResponse } from \"next\";\nimport configConstants, { HELLOMOON_CLIENT } from \"../../constants\";\nconfigConstants();\nimport { Comparison, buildComparison } from \"@/lib/helloMoon\";\nimport { JupiterHistoricalTradingStatsRequest } from \"@hellomoon/api\";\nimport { makeApiPostRequest } from \"@/lib/middleware\";\n\nconst VALID_GRANULARITY = [\"DAILY\", \"WEEKLY\", \"MONTHLY\"];\nasync function handler(req: NextApiRequest, res: NextApiResponse) {\n  const { granularity, limit } = req.body;\n\n  if (granularity && !VALID_GRANULARITY.includes((granularity as string).toLocaleUpperCase())) {\n    res\n      .status(500)\n      .send(\n        \"Invalid granularity: \" +\n          granularity +\n          \". Valid granularities are: \" +\n          VALID_GRANULARITY.join(\", \") +\n          \".\",\n      );\n    return;\n  }\n\n  let numTxns: Comparison;\n  let usdVolume: Comparison;\n  let numUsers: Comparison;\n\n  try {\n    numTxns = buildComparison(req.body.numTxnsOperator, req.body.numTxnsValue);\n    usdVolume = buildComparison(req.body.usdVolumeOperator, req.body.usdVolumeValue);\n    numUsers = buildComparison(req.body.numUsersOperator, req.body.numUsersValue);\n  } catch (error) {\n    res.status(500).send((error as Error).message);\n    return;\n  }\n\n  let args = new JupiterHistoricalTradingStatsRequest({\n    granularity: ((granularity as string).toLocaleUpperCase() as any) ?? \"DAILY\",\n    limit: limit ?? 10,\n    numTxns,\n    usdVolume,\n    numUsers,\n  });\n\n  let data = await HELLOMOON_CLIENT.send(args)\n    .then(result => result.data)\n    .catch(console.error);\n\n  res.status(200).send(data);\n}\n\nexport default makeApiPostRequest(handler);\n"
  },
  {
    "path": "chatgpt-plugin/src/pages/api/helloMoon/jupiter/pairs.ts",
    "content": "// DEPRECATED - not in use\nimport { NextApiRequest, NextApiResponse } from \"next\";\nimport configConstants, { HELLOMOON_CLIENT } from \"../../constants\";\nimport { Comparison, buildComparison, cleanSwapPair } from \"@/lib/helloMoon\";\n\nimport { JupiterSwapStatsRequest } from \"@hellomoon/api\";\nconfigConstants();\n\nexport default async function handler(req: NextApiRequest, res: NextApiResponse) {\n  const { swapPair, limit } = req.body;\n\n  let dailyVolume: Comparison;\n  let weeklyVolume: Comparison;\n  let monthlyVolume: Comparison;\n\n  try {\n    dailyVolume = buildComparison(req.body.usdVolume24HrOperator, req.body.usdVolume24HrValue);\n    weeklyVolume = buildComparison(req.body.usdVolume7DOperator, req.body.usdVolume7DValue);\n    monthlyVolume = buildComparison(req.body.usdVolume30DOperator, req.body.usdVolume30DValue);\n  } catch (error) {\n    res.status(500).send((error as Error).message);\n    return;\n  }\n\n  let args = new JupiterSwapStatsRequest({\n    swapPair: cleanSwapPair(swapPair),\n    limit: limit ?? 10,\n    usdVolume24Hr: dailyVolume,\n    usdVolume7D: weeklyVolume,\n    usdVolume30D: monthlyVolume,\n  });\n\n  let data = await HELLOMOON_CLIENT.send(args)\n    .then(result => result.data)\n    .catch(console.error);\n\n  res.status(200).send(data);\n}\n"
  },
  {
    "path": "chatgpt-plugin/src/pages/api/helloMoon/jupiter/summary.ts",
    "content": "import { NextApiRequest, NextApiResponse } from \"next\";\nimport configConstants, { HELLOMOON_CLIENT } from \"../../constants\";\nimport { JupiterCurrentStatsRequest } from \"@hellomoon/api\";\nimport { makeApiPostRequest } from \"@/lib/middleware\";\nconfigConstants();\n\nasync function handler(req: NextApiRequest, res: NextApiResponse) {\n  let data = await HELLOMOON_CLIENT.send(new JupiterCurrentStatsRequest({}))\n    .then(result => result.data[0])\n    .catch(console.error);\n\n  res.status(200).send(data);\n}\n\nexport default makeApiPostRequest(handler);\n"
  },
  {
    "path": "chatgpt-plugin/src/pages/api/helloMoon/jupiter/swaps.ts",
    "content": "import { NextApiRequest, NextApiResponse } from \"next\";\nimport configConstants, { HELLOMOON_CLIENT } from \"../../constants\";\nconfigConstants();\n\nimport { JupiterPairsBrokenDownWeeklyRequest } from \"@hellomoon/api\";\nimport { cleanSwapPair } from \"@/lib/helloMoon\";\nimport { makeApiPostRequest } from \"@/lib/middleware\";\n\nconst VALID_CATEGORY = [\"per amm\", \"whole market\", \"jupiter only\"];\n// Make params case-insensitive so it's easier for LLMs to get right\nconst CATEGORY_MAP: { [key: string]: string } = {\n  \"per amm\": \"Per AMM\",\n  \"whole market\": \"Whole market\",\n  \"jupiter only\": \"Jupiter only\",\n};\n\nasync function handler(req: NextApiRequest, res: NextApiResponse) {\n  const { category, swapPair, limit } = req.body;\n\n  if (category && !VALID_CATEGORY.includes((category as string).toLocaleLowerCase())) {\n    res\n      .status(500)\n      .send(\n        \"Invalid category: \" +\n          category +\n          \". Valid categories are: \" +\n          VALID_CATEGORY.join(\", \") +\n          \".\",\n      );\n    return;\n  }\n\n  let mapped = CATEGORY_MAP[category as string] as any;\n  let args = new JupiterPairsBrokenDownWeeklyRequest({\n    category: mapped,\n    swapPair: swapPair ? cleanSwapPair(swapPair) : undefined,\n    limit,\n  });\n\n  let data = await HELLOMOON_CLIENT.send(args)\n    .then(result => result.data)\n    .catch(console.error);\n\n  res.status(200).send(data);\n}\n\nexport default makeApiPostRequest(handler);\n"
  },
  {
    "path": "chatgpt-plugin/src/pages/api/helloMoon/jupiter.yaml",
    "content": "openapi: 3.1.0\ninfo:\n  title: HelloMoon API\n  description: An API for querying information about protocols built on Solana\n  version: 1.0.0\nservers:\n  - url: https://chatgpt.solanalabs.com/api/helloMoon\npaths:\n  /jupiter/summary:\n    post:\n      summary: Shows high-level information related to DeFi protocol Jupiter on Solana\n      description:\n        Shows the volume, number of users, and number of transactions on Jupiter over the past 24\n        hours, 7 days, and 30 days in USD\n      operationId: query_jupiter_summary\n      responses:\n        \"200\":\n          description: Successful Response\n  /jupiter/swaps:\n    post:\n      summary: Shows swap data on Jupiter protocol by amm\n      description:\n        Breakdown of swapping pair volume weekly on and off of Jupiter weekly. Data goes back 90\n        days. Data can be broken down by AMM if needed. Note that swapPair is the combination of the\n        source and destination symbol. The order will be in alphabetical order. For example, \"USDC -\n        wSOL\"\n      operationId: query_jupiter_swap_summary\n      requestBody:\n        content:\n          application/json:\n            schema:\n              $ref: \"#/components/schemas/jupiterSwapRequest\"\n      responses:\n        \"200\":\n          description: Successful Response\n  /jupiter/historical:\n    post:\n      summary: Shows the volume, number of users, and number of transactions on Jupiter over time\n      description:\n        Shows the volume, number of users, and number of transactions on Jupiter over time\n      operationId: query_jupiter_historical_summary\n      requestBody:\n        content:\n          application/json:\n            schema:\n              $ref: \"#/components/schemas/jupiterHistoricalRequest\"\n      responses:\n        \"200\":\n          description: Successful Response\n  /jupiter/pairs:\n    post:\n      summary: Shows the volume of swaps for token pairs on Jupiter\n      description:\n        Shows the volume of swaps for token pairs on Jupiter. Note that swapPair is the combination\n        of the source and destination symbol. The order will be in alphabetical order. For example,\n        \"USDC - wSOL\". Note that \"USD\" is not a valid token, and should be converted to \"USDC\" when\n        using this API. Also, 24hr is a day, 7D is a week, 30D is a month.\n      operationId: query_jupiter_pair_summary\n      requestBody:\n        content:\n          application/json:\n            schema:\n              $ref: \"#/components/schemas/jupiterPairsRequest\"\n      responses:\n        \"200\":\n          description: Successful Response\ncomponents:\n  schemas:\n    stringComparisonOperator:\n      type: string\n      enum:\n        - \"=\"\n        - \"!=\"\n        - \">\"\n        - \"<\"\n        - \">=\"\n      nullable: true\n    nullableNumber:\n      type: number\n      nullable: true\n    jupiterSwapRequest:\n      type: object\n      properties:\n        category:\n          type: string\n          enum:\n            - \"whole market\"\n            - \"jupiter only\"\n            - \"per amm\"\n          nullable: true\n        ammProgramId:\n          type: string\n          nullable: true\n        swapPair:\n          type: string\n          nullable: true\n        limit:\n          type: number\n          nullable: true\n    jupiterHistoricalRequest:\n      type: object\n      properties:\n        granularity:\n          type: string\n          enum:\n            - \"daily\"\n            - \"weekly\"\n            - \"monthly\"\n          nullable: true\n        limit:\n          $ref: \"#/components/schemas/nullableNumber\"\n        numTxnsOperator:\n          $ref: \"#/components/schemas/stringComparisonOperator\"\n        numTxnsValue:\n          $ref: \"#/components/schemas/nullableNumber\"\n        usdVolumeValue:\n          $ref: \"#/components/schemas/nullableNumber\"\n        usdVolumeOperator:\n          $ref: \"#/components/schemas/stringComparisonOperator\"\n        numUsersValue:\n          $ref: \"#/components/schemas/nullableNumber\"\n        numUsersOperator:\n          $ref: \"#/components/schemas/stringComparisonOperator\"\n    jupiterPairsRequest:\n      type: object\n      properties:\n        swapPair:\n          type: string\n          nullable: true\n        limit:\n          $ref: \"#/components/schemas/nullableNumber\"\n        usdVolume7DOperator:\n          $ref: \"#/components/schemas/stringComparisonOperator\"\n        usdVolume30DOperator:\n          $ref: \"#/components/schemas/stringComparisonOperator\"\n        usdVolume24HrOperator:\n          $ref: \"#/components/schemas/stringComparisonOperator\"\n        usdVolume7DValue:\n          $ref: \"#/components/schemas/nullableNumber\"\n        usdVolume30DValue:\n          $ref: \"#/components/schemas/nullableNumber\"\n        usdVolume24HrValue:\n          $ref: \"#/components/schemas/nullableNumber\"\n"
  },
  {
    "path": "chatgpt-plugin/src/pages/api/metaplex/getCNFTRent/index.ts",
    "content": "// Deprecated - Not in use\nimport {\n  ALL_DEPTH_SIZE_PAIRS,\n  DepthSizePair,\n  getConcurrentMerkleTreeAccountSize,\n} from \"@solana/spl-account-compression\";\nimport { LAMPORTS_PER_SOL } from \"@solana/web3.js\";\nimport { NextApiRequest, NextApiResponse } from \"next\";\nimport configConstants, { CONNECTION } from \"../../constants\";\nconfigConstants();\n\nfunction isValidDepthSizePair(maxDepth: number, maxBufferSize: number): boolean {\n  const pair: DepthSizePair = {\n    maxDepth,\n    maxBufferSize,\n  };\n  return ALL_DEPTH_SIZE_PAIRS.some(validPair => isEqualDepthSizePair(validPair, pair));\n}\n\nfunction isEqualDepthSizePair(pair1: DepthSizePair, pair2: DepthSizePair): boolean {\n  return pair1.maxDepth === pair2.maxDepth && pair1.maxBufferSize === pair2.maxBufferSize;\n}\n\nexport default async function handler(req: NextApiRequest, res: NextApiResponse) {\n  if (req.method != \"POST\") {\n    res.status(405).send({ message: \"Only POST requests allowed\" });\n    return;\n  }\n\n  const { treeSize, canopySize, customMaxDepth, customMaxBufferSize, customCanopyDepth } = req.body;\n  let maxDepth: number, maxBufferSize: number, canopyDepth: number;\n  if (treeSize.toLowerCase() == \"custom\") {\n    if (customMaxDepth == undefined || customMaxBufferSize == undefined) {\n      res.status(400).send({ message: \"Missing tree size custom parameters\" });\n      return;\n    }\n    maxDepth = customMaxDepth;\n    maxBufferSize = customMaxBufferSize;\n  } else if (treeSize.toLowerCase() == \"small\") {\n    maxDepth = 14;\n    maxBufferSize = 64;\n  } else if (treeSize.toLowerCase() == \"medium\") {\n    maxDepth = 20;\n    maxBufferSize = 64;\n  } else if (treeSize.toLowerCase() == \"large\") {\n    maxDepth = 30;\n    maxBufferSize = 512;\n  } else {\n    res.status(400).send({ message: \"Invalid tree size enum\" });\n    return;\n  }\n\n  if (!isValidDepthSizePair(maxDepth, maxBufferSize)) {\n    res.status(400).send({ message: \"Invalid tree Depth and Buffer Size pair\" });\n    return;\n  }\n\n  if (canopySize.toLowerCase() == \"custom\") {\n    if (customCanopyDepth == undefined) {\n      res.status(400).send({ message: \"Missing custom canopy depth parameter\" });\n      return;\n    }\n    canopyDepth = customCanopyDepth;\n  } else if (canopySize.toLowerCase() == \"small\") {\n    canopyDepth = 3;\n  } else if (canopySize.toLowerCase() == \"medium\") {\n    canopyDepth = 14;\n  } else if (canopySize.toLowerCase() == \"large\") {\n    canopyDepth = 17;\n  } else if (canopySize.toLowerCase() == \"none\") {\n    canopyDepth = 0;\n  } else {\n    res.status(400).send({ message: \"Invalid canopy size enum\" });\n    return;\n  }\n\n  const requiredSpace = getConcurrentMerkleTreeAccountSize(maxDepth, maxBufferSize, canopyDepth);\n\n  const rent = await CONNECTION.getMinimumBalanceForRentExemption(requiredSpace);\n  res.status(200).send({\n    rentInSol: rent / LAMPORTS_PER_SOL,\n    maxNumberOfNFTs: 2 ** maxDepth,\n  });\n}\n"
  },
  {
    "path": "chatgpt-plugin/src/pages/api/metaplex/getPublicTree/index.ts",
    "content": "// Deprecated - Not in use\nimport { NextApiRequest, NextApiResponse } from \"next\";\nimport configConstants from \"../../constants\";\nconfigConstants();\n\nconst publicTreeAddresses: String[] = [\n  \"3cnMCu5hezx9h6kjEYdTb7PRKch4a6KRFBoUF8SJEsT8\",\n  \"GNsnin9c2nDGp78E69tGXyMScWfysnu2PuxQxXy1jh3R\",\n];\n\nexport default async function handler(req: NextApiRequest, res: NextApiResponse) {\n  res.status(200).send({ publicTreeAddresses: publicTreeAddresses });\n}\n"
  },
  {
    "path": "chatgpt-plugin/src/pages/api/metaplex/metaplex.yaml",
    "content": "openapi: 3.0.2\ninfo:\n  title: Metaplex API\n  description: An API for interacting with the Solana NFT collections using Metaplex\n  version: 1.0.0\nservers:\n  - url: https://chatgpt.solanalabs.com\npaths:\n  /api/handlers/solana-pay/qr/createMintCNFT:\n    post:\n      summary: createMintCNFT\n      description: >-\n        Mints a compressed NFT with the metadata uri. It does not need the rent or the treeAddress.\n        Returns a QR code that the user can scan to approve the transaction.\n      operationId: create_mint_cnft\n      requestBody:\n        content:\n          application/json:\n            schema:\n              $ref: \"#/components/schemas/createMintCNFTRequest\"\n      responses:\n        \"200\":\n          description: Successful Response\n  /api/metaplex/getCNFTRent:\n    post:\n      summary: getCNFTRent\n      description: >-\n        Returns the rent for creating a tree on mainnet and the maximum capacity.\n      operationId: get_cnft_rent\n      requestBody:\n        content:\n          application/json:\n            schema:\n              $ref: \"#/components/schemas/getCNFTRentRequest\"\n      responses:\n        \"200\":\n          description: Successful Response\n  /api/metaplex/getPublicTree:\n    post:\n      summary: getPublicTree\n      description: >-\n        Shows the public tree addresses with available space.\n      operationId: get_public_tree\n      requestBody:\n        content:\n          application/json:\n            schema:\n              $ref: \"#/components/schemas/getPublicTreeRequest\"\n      responses:\n        \"200\":\n          description: Successful Response\n  /api/handlers/solana-pay/qr/createMintNFT:\n    post:\n      summary: createMintNFT\n      description: >-\n        Mints a normal uncompressed NFT with the given name, metadata uri and seller fees.\n      operationId: create_mint_nft\n      requestBody:\n        content:\n          application/json:\n            schema:\n              $ref: \"#/components/schemas/createMintNFTRequest\"\n      responses:\n        \"200\":\n          description: Successful Response\n  /api/handlers/solana-pay/qr/createTransferAsset:\n    post:\n      summary: createTransferAsset\n      description: >-\n        Allows the user to send (transfer) compressed NFT to another account\n      operationId: create_transfer_asset\n      requestBody:\n        content:\n          application/json:\n            schema:\n              $ref: \"#/components/schemas/createTransferAssetRequest\"\n      responses:\n        \"200\":\n          description: Successful Response\n  /api/handlers/solana-pay/qr/createBurnAsset:\n    post:\n      summary: createBurnAsset\n      description: >-\n        Allows the user to burn a compressed NFT\n      operationId: create_burn_asset\n      requestBody:\n        content:\n          application/json:\n            schema:\n              $ref: \"#/components/schemas/createBurnAssetRequest\"\n      responses:\n        \"200\":\n          description: Successful Response\ncomponents:\n  schemas:\n    getCNFTRentRequest:\n      title: GetCNFTRentRequest\n      type: object\n      required:\n        - treeSize\n        - canopySize\n      properties:\n        treeSize:\n          type: string\n          enum:\n            - \"custom\"\n            - \"small\"\n            - \"medium\"\n            - \"large\"\n        canopySize:\n          type: string\n          enum:\n            - \"custom\"\n            - \"small\"\n            - \"medium\"\n            - \"large\"\n            - \"none\"\n        customMaxDepth:\n          type: number\n          nullable: true\n        customMaxBufferSize:\n          type: number\n          nullable: true\n        customCanopySize:\n          type: number\n          nullable: true\n    createMintCNFTRequest:\n      title: CreateMintCNFTRequest\n      type: object\n      required:\n        - metadataUri\n      properties:\n        metadataUri:\n          type: \"string\"\n          format: \"uri\"\n          maxLength: 255\n    createMintNFTRequest:\n      title: CreateMintNFTRequest\n      type: object\n      required:\n        - name\n        - metadataUri\n      properties:\n        name:\n          type: string\n        metadataUri:\n          type: string\n          format: \"uri\"\n          maxLength: 255\n        sellerFee:\n          type: number\n    createTransferAssetRequest:\n      type: object\n      required:\n        - destination\n        - assetId\n      properties:\n        destination:\n          type: string\n        assetId:\n          type: string\n    createBurnAssetRequest:\n      type: object\n      required:\n        - assetId\n      properties:\n        assetId:\n          type: string\n    getPublicTreeRequest:\n      type: object\n      properties: {}\n"
  },
  {
    "path": "chatgpt-plugin/src/pages/api/solflare-pfp/getProfilePic.ts",
    "content": "// Deprecated - Not in use\nimport { NextApiRequest, NextApiResponse } from \"next\";\nimport { getProfilePicture } from \"@solflare-wallet/pfp\";\nimport configConstants, { CONNECTION } from \"../constants\";\nconfigConstants();\n\nexport default async function handler(req: NextApiRequest, res: NextApiResponse) {\n  if (req.method != \"POST\") {\n    res.status(405).send({ message: \"Only POST requests allowed\" });\n    return;\n  }\n\n  let publicKey = req.body.publicKey;\n  let pfpResult = await getProfilePicture(CONNECTION, publicKey);\n  res.status(200).send(pfpResult);\n}\n"
  },
  {
    "path": "chatgpt-plugin/src/pages/api/tiplink/makeLink.ts",
    "content": "import { NextApiRequest, NextApiResponse } from \"next\";\nimport { TipLink } from \"@tiplink/api\";\nimport { makeApiPostRequest } from \"@/lib/middleware\";\n\nasync function handler(req: NextApiRequest, res: NextApiResponse) {\n  const tp = await TipLink.create();\n  res.status(200).send({ url: tp.url, tipLinkAddress: tp.keypair.publicKey.toBase58() });\n}\n\nexport default makeApiPostRequest(handler);\n"
  },
  {
    "path": "chatgpt-plugin/tailwind.config.js",
    "content": "/** @type {import('tailwindcss').Config} */\nmodule.exports = {\n  content: [\n    './src/pages/**/*.{js,ts,jsx,tsx,mdx}',\n    './src/components/**/*.{js,ts,jsx,tsx,mdx}',\n    './src/app/**/*.{js,ts,jsx,tsx,mdx}',\n  ],\n  theme: {\n    extend: {\n      backgroundImage: {\n        'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',\n        'gradient-conic':\n          'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',\n      },\n    },\n  },\n  plugins: [],\n}\n"
  },
  {
    "path": "langchain_examples/README.md",
    "content": "# Langchain Usage\n\nLangchain has support for using ChatGPTs in their Agent via their `tool` interface.\n\nWe have provided examples below\n\n### Python Example\n\nTo run the python example, you will have to have an OpenAI key (you do not need GPT-4 access to run this example).\n\n```bash\npoetry install\ncd langchain_examples\nOPENAI_API_KEY=sk-xxxxxxx DEV=true python python/main.py\n```"
  },
  {
    "path": "langchain_examples/python/main.py",
    "content": "from langchain.chat_models import ChatOpenAI\nfrom langchain.agents import load_tools, initialize_agent\nfrom langchain.agents import AgentType\nfrom langchain.tools import AIPluginTool\n\n\nif __name__ == '__main__':\n    # Setup environment variables\n    import os\n    DEV_ENV = os.environ['DEV'] == 'true'\n    URL = \"http://localhost:3333\" if DEV_ENV else \"https://solana-gpt-plugin.onrender.com\"\n\n    llm = ChatOpenAI(temperature=0)\n\n    # AI Agent does best when it only has one available tool\n    # to engage with URLs\n    tools = load_tools([\"requests_post\"])\n\n    # AIPluginTool only fetches and returns the openapi.yaml linked to in /.well-known/ai-plugin.json\n    # This may need some more work to avoid blowing up LLM context window\n    tool = AIPluginTool.from_plugin_url(URL + \"/.well-known/ai-plugin.json\")\n    tools += [tool]\n\n    # Setup an agent to answer the question without further human feedback\n    agent_chain = initialize_agent(\n        tools, llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True)\n\n    # Ask the question, and the agent loop\n    agent_chain.run(\n        \"How many lamports does 8fbqVvpK3Dj7fdP2c8JJhtD7Zy3n9qtwAeGfbkgPu625 have?\")\n"
  },
  {
    "path": "pyproject.toml",
    "content": "[tool.poetry]\nname = \"solana-gpt-plugin\"\nversion = \"0.1.0\"\ndescription = \"\"\nauthors = [\"ngundotra <noah@gundotra.org>\"]\nlicense = \"Apache-2.0\"\nreadme = \"README.md\"\npackages = [{include = \"solana_gpt_plugin\"}]\n\n[tool.poetry.dependencies]\npython = \"^3.9\"\nlangchain = \"^0.0.136\"\nrequests = \"^2.28.2\"\nopenai = \"^0.27.4\"\n\n\n[build-system]\nrequires = [\"poetry-core\"]\nbuild-backend = \"poetry.core.masonry.api\"\n"
  },
  {
    "path": "scripts/createNFTCollection.ts",
    "content": "import {\n  Metaplex,\n  toMetaplexFile,\n  UploadMetadataInput,\n} from \"@metaplex-foundation/js\";\nimport {\n  createAssociatedTokenAccount,\n  createMint,\n  mintTo,\n} from \"@solana/spl-token\";\nimport {\n  Keypair,\n  sendAndConfirmTransaction,\n  Transaction,\n} from \"@solana/web3.js\";\nimport { savePublicKeyToFile } from \"./helper\";\n\nimport { PublicKey } from \"@metaplex-foundation/js\";\n\nimport {\n  createCreateMasterEditionV3Instruction,\n  createCreateMetadataAccountV3Instruction,\n  CreateMetadataAccountArgsV3,\n  createSetCollectionSizeInstruction,\n  PROGRAM_ID as TOKEN_METADATA_PROGRAM_ID,\n} from \"@metaplex-foundation/mpl-token-metadata\";\nimport * as fs from \"fs\";\n\nimport dotenv from \"dotenv\";\ndotenv.config();\n\nconst ASSETS_DIR = \"../chatgpt-plugin/public/assets/\";\nexport async function createNFTCollection(payer: Keypair, metaplex: Metaplex) {\n  console.log(metaplex.connection);\n  // Create collection metadata\n  const buffer = fs.readFileSync(`${ASSETS_DIR}chatgpt-collection-logo.png`);\n  const file = toMetaplexFile(buffer, `chatgpt-collection-logo.png`);\n  console.log(\"before Uploading file to Metaplex\");\n  const imageUri = await metaplex.storage().upload(file);\n  console.log(\"after Uploading file to Metaplex\");\n  const data = fs.readFileSync(\n    `${ASSETS_DIR}chatgpt-collection-metadata.json`,\n    \"utf-8\"\n  );\n  const collectionInfo = JSON.parse(data);\n\n  const collectionMetadata: UploadMetadataInput = {\n    name: collectionInfo.name,\n    symbol: collectionInfo.symbol,\n    image: imageUri,\n    description: collectionInfo.description,\n  };\n  console.log(\"before Uploading metadata to Metaplex\");\n  const { uri } = await metaplex.nfts().uploadMetadata(collectionMetadata);\n  console.log(\"after Uploading metadata to Metaplex\");\n  // Create collection\n  const collectionMetadataV3: CreateMetadataAccountArgsV3 = {\n    data: {\n      name: `${collectionMetadata.name ?? \"\"}`,\n      symbol: `${collectionMetadata.symbol ?? \"\"}`,\n      uri,\n      sellerFeeBasisPoints: 100,\n      creators: [\n        {\n          address: payer.publicKey,\n          verified: true,\n          share: 100,\n        },\n      ],\n      collection: null,\n      uses: null,\n    },\n    isMutable: false,\n    collectionDetails: null,\n  };\n  console.log(\"before creating mint\");\n  const collectionMint = await createMint(\n    metaplex.connection,\n    payer,\n    payer.publicKey, // mintAuthority\n    payer.publicKey, // freezeAuthority\n    0 // collection -> decimal == 0\n  );\n  console.log(\"before creating account\");\n  // const tokenAccount = await createAccount(\n  //   metaplex.connection,\n  //   payer,\n  //   collectionMint,\n  //   payer.publicKey,\n  // );\n  console.log(collectionMint.toString());\n  const tokenAccount = await createAssociatedTokenAccount(\n    metaplex.connection,\n    payer,\n    collectionMint,\n    payer.publicKey,\n    { commitment: \"confirmed\" }\n  );\n\n  console.log(\"before minting\");\n  await mintTo(\n    metaplex.connection,\n    payer,\n    collectionMint,\n    tokenAccount,\n    payer,\n    1,\n    [],\n    { commitment: \"confirmed\" }\n  );\n  console.log(\"after minting\");\n  const [collectionMetadataAccount, _bump] = PublicKey.findProgramAddressSync(\n    [\n      Buffer.from(\"metadata\", \"utf8\"),\n      TOKEN_METADATA_PROGRAM_ID.toBuffer(),\n      collectionMint.toBuffer(),\n    ],\n    TOKEN_METADATA_PROGRAM_ID\n  );\n\n  const createMetadataIx = createCreateMetadataAccountV3Instruction(\n    {\n      metadata: collectionMetadataAccount,\n      mint: collectionMint,\n      mintAuthority: payer.publicKey,\n      payer: payer.publicKey,\n      updateAuthority: payer.publicKey,\n    },\n    {\n      createMetadataAccountArgsV3: collectionMetadataV3,\n    }\n  );\n\n  // create account for showing supply of collection metadata, the proof of the Non-Fungible of the token\n  const [collectionMasterEditionAccount, _bump2] =\n    PublicKey.findProgramAddressSync(\n      [\n        Buffer.from(\"metadata\", \"utf8\"),\n        TOKEN_METADATA_PROGRAM_ID.toBuffer(),\n        collectionMint.toBuffer(),\n        Buffer.from(\"edition\", \"utf8\"),\n      ],\n      TOKEN_METADATA_PROGRAM_ID\n    );\n\n  const createMasterEditionIx = createCreateMasterEditionV3Instruction(\n    {\n      edition: collectionMasterEditionAccount,\n      payer: payer.publicKey,\n      mint: collectionMint,\n      mintAuthority: payer.publicKey,\n      updateAuthority: payer.publicKey,\n      metadata: collectionMetadataAccount,\n    },\n    {\n      createMasterEditionArgs: {\n        maxSupply: 0,\n      },\n    }\n  );\n\n  // create collection size\n  const collectionSizeIx = createSetCollectionSizeInstruction(\n    {\n      collectionMetadata: collectionMetadataAccount,\n      collectionAuthority: payer.publicKey,\n      collectionMint: collectionMint,\n    },\n    {\n      setCollectionSizeArgs: { size: 1000000 },\n    }\n  );\n\n  try {\n    const tx = new Transaction();\n    tx.add(createMetadataIx);\n    tx.add(createMasterEditionIx);\n    tx.add(collectionSizeIx);\n\n    tx.feePayer = payer.publicKey;\n    // tx.sign(payer); ??\n\n    await sendAndConfirmTransaction(metaplex.connection, tx, [payer], {\n      commitment: \"confirmed\",\n      skipPreflight: true, //?\n    });\n  } catch (e) {\n    console.error(\"\\nFailed to create collection:\", e);\n    throw e;\n  }\n  savePublicKeyToFile(\n    \"collectionMint\",\n    collectionMint,\n    `${ASSETS_DIR}/chatgpt-collection-keys.json`\n  );\n  savePublicKeyToFile(\n    \"collectionMetadataAccount\",\n    collectionMetadataAccount,\n    `${ASSETS_DIR}chatgpt-collection-keys.json`\n  );\n  savePublicKeyToFile(\n    \"collectionMasterEditionAccount\",\n    collectionMasterEditionAccount,\n    `${ASSETS_DIR}chatgpt-collection-keys.json`\n  );\n  console.log(\"Collection Mint Address: \", collectionMint.toBase58());\n  console.log(\n    \"Collection Metadata Address: \",\n    collectionMetadataAccount.toBase58()\n  );\n  console.log(\n    \"Collection Master Edition Address: \",\n    collectionMasterEditionAccount.toBase58()\n  );\n  return {\n    collectionMint,\n    collectionMetadataAccount,\n    collectionMasterEditionAccount,\n  };\n}\n"
  },
  {
    "path": "scripts/createTree.ts",
    "content": "import {\n    Keypair,\n    Connection,\n    PublicKey,\n    Transaction,\n    sendAndConfirmTransaction,\n  } from \"@solana/web3.js\";\n  import {\n    ValidDepthSizePair,\n    createAllocTreeIx,\n    SPL_ACCOUNT_COMPRESSION_PROGRAM_ID,\n    SPL_NOOP_PROGRAM_ID,\n  } from \"@solana/spl-account-compression\";\n  \n  import {\n    PROGRAM_ID as BUBBLEGUM_PROGRAM_ID,\n    createCreateTreeInstruction,\n  } from \"@metaplex-foundation/mpl-bubblegum\";\n  import { savePublicKeyToFile } from \"./helper\";\n  import dotenv from \"dotenv\";\n  dotenv.config();\n  \n  export interface MerkleTreeArgs {\n    maxDepth: number;\n    maxBufferSize: number;\n    canopyHeight: number;\n  }\n  \n  export async function createTree(\n    payer: Keypair,\n    connection: Connection,\n    treeNumber: number | null,\n    canopyDepth: number,\n    maxDepthSizePair: ValidDepthSizePair\n  ) {\n    const treeKeypair = Keypair.generate();\n  \n    const [treeAuthority, _bump] = PublicKey.findProgramAddressSync(\n      [treeKeypair.publicKey.toBuffer()],\n      BUBBLEGUM_PROGRAM_ID\n    );\n  \n    // allocate space for tree account instruction\n    const allocTreeIx = await createAllocTreeIx(\n      connection,\n      treeKeypair.publicKey,\n      payer.publicKey,\n      maxDepthSizePair,\n      canopyDepth\n    );\n  \n    // create tree\n    const createTreeIx = createCreateTreeInstruction(\n      {\n        payer: payer.publicKey,\n        treeCreator: payer.publicKey,\n        treeAuthority: treeAuthority,\n        merkleTree: treeKeypair.publicKey,\n        compressionProgram: SPL_ACCOUNT_COMPRESSION_PROGRAM_ID,\n        logWrapper: SPL_NOOP_PROGRAM_ID,\n      },\n      {\n        maxBufferSize: maxDepthSizePair.maxBufferSize,\n        maxDepth: maxDepthSizePair.maxDepth,\n        public: true,\n      },\n      BUBBLEGUM_PROGRAM_ID\n    );\n  \n    try {\n      const tx = new Transaction();\n      tx.add(allocTreeIx);\n      tx.add(createTreeIx);\n      tx.feePayer = payer.publicKey;\n  \n      await sendAndConfirmTransaction(connection, tx, [payer, treeKeypair], {\n        skipPreflight: true,\n        commitment: \"confirmed\",\n      });\n    } catch (e) {\n      console.error(e);\n      throw e;\n    }\n    savePublicKeyToFile(`treeAddress${treeNumber}`, treeKeypair.publicKey, \"./assets/chatgpt-collection-keys.json\");\n    savePublicKeyToFile(`treeAuthority${treeNumber}`, treeAuthority, \"./assets/chatgpt-collection-keys.json\");\n    console.log(`Tree Address${treeNumber}: `, treeKeypair.publicKey.toBase58());\n    return { treeAddress: treeKeypair.publicKey, treeAuthority };\n  }"
  },
  {
    "path": "scripts/helper.ts",
    "content": "import fs from \"fs\";\nimport path from \"path\";\nimport {\n  Connection,\n  Keypair,\n  LAMPORTS_PER_SOL,\n  PublicKey,\n} from \"@solana/web3.js\";\n\n// define some default locations\nconst DEFAULT_KEY_DIR_NAME = \".local_keys\";\nconst DEFAULT_PUBLIC_KEY_FILE = \"keys.json\";\nconst DEFAULT_NONCE_FILE = \"nonce.json\";\n\n/*\n  Load locally stored PublicKey addresses\n*/\nexport function loadPublicKeysFromFile(\n  absPath: string = `${DEFAULT_KEY_DIR_NAME}/${DEFAULT_PUBLIC_KEY_FILE}`\n) {\n  try {\n    if (!absPath) throw Error(\"No path provided\");\n    if (!fs.existsSync(absPath)) throw Error(\"File does not exist.\");\n\n    // load the public keys from the file\n    const data =\n      JSON.parse(fs.readFileSync(absPath, { encoding: \"utf-8\" })) || {};\n\n    // convert all loaded keyed values into valid public keys\n    for (const [key, value] of Object.entries(data)) {\n      data[key] = new PublicKey(value as string) ?? \"\";\n    }\n\n    return data;\n  } catch (err) {\n    // console.warn(\"Unable to load local file\");\n  }\n  // always return an object\n  return {};\n}\n\n/*\n  Locally save a PublicKey addresses to the filesystem for later retrieval\n*/\nexport function savePublicKeyToFile(\n  name: string,\n  publicKey: PublicKey,\n  absPath: string = `${DEFAULT_KEY_DIR_NAME}/${DEFAULT_PUBLIC_KEY_FILE}`\n) {\n  try {\n    // if (!absPath) throw Error(\"No path provided\");\n    // if (!fs.existsSync(absPath)) throw Error(\"File does not exist.\");\n\n    // fetch all the current values\n    let data: any = loadPublicKeysFromFile(absPath);\n\n    // convert all loaded keyed values from PublicKeys to strings\n    for (const [key, value] of Object.entries(data)) {\n      data[key as any] = (value as PublicKey).toBase58();\n    }\n    data = { ...data, [name]: publicKey.toBase58() };\n\n    // actually save the data to the file\n    fs.writeFileSync(absPath, JSON.stringify(data), {\n      encoding: \"utf-8\",\n    });\n    console.log(\"Saved to file: \", absPath);\n\n    // reload the keys for sanity\n    data = loadPublicKeysFromFile(absPath);\n\n    return data;\n  } catch (err) {\n    console.warn(\"Unable to save to file\");\n  }\n  // always return an object\n  return {};\n}\n\n/*\n  Load locally stored nonce of nft counr\n*/\nexport function loadNonceFromFile(\n  absPath: string = `${DEFAULT_KEY_DIR_NAME}/${DEFAULT_NONCE_FILE}`\n) {\n  try {\n    if (!absPath) throw Error(\"No path provided\");\n    if (!fs.existsSync(absPath)) throw Error(\"File does not exist.\");\n\n    // load the public keys from the file\n    const data =\n      JSON.parse(fs.readFileSync(absPath, { encoding: \"utf-8\" })) || {};\n\n    // convert all loaded keyed values into valid public keys\n    for (const [key, value] of Object.entries(data)) {\n      data[key] = value as number;\n    }\n\n    return data;\n  } catch (err) {\n    // console.warn(\"Unable to load local file\");\n  }\n  // always return an object\n  return {};\n}\n\n/*\n  Locally save a PublicKey addresses to the filesystem for later retrieval\n*/\nexport function saveNonceToFile(\n  nonce: number,\n  absPath: string = `${DEFAULT_KEY_DIR_NAME}/${DEFAULT_NONCE_FILE}`\n) {\n  try {\n    // if (!absPath) throw Error(\"No path provided\");\n    // if (!fs.existsSync(absPath)) throw Error(\"File does not exist.\");\n\n    // fetch all the current values\n    let data: any = loadNonceFromFile(absPath);\n\n    // convert all loaded keyed values from PublicKeys to strings\n    for (const [key, value] of Object.entries(data)) {\n      data[key as any] = value as number;\n    }\n    data = { nonce };\n\n    // actually save the data to the file\n    fs.writeFileSync(absPath, JSON.stringify(data), {\n      encoding: \"utf-8\",\n    });\n\n    // reload the keys for sanity\n    data = loadPublicKeysFromFile(absPath);\n\n    return data;\n  } catch (err) {\n    console.warn(\"Unable to save to file\");\n  }\n  // always return an object\n  return {};\n}"
  },
  {
    "path": "scripts/runCreateCollection.ts",
    "content": "import {\n  Metaplex,\n  bundlrStorage,\n  keypairIdentity,\n} from \"@metaplex-foundation/js\";\nimport { Connection, Keypair } from \"@solana/web3.js\";\nimport dotenv from \"dotenv\";\nimport { uploadMetadata } from \"./uploadMetadata\";\ndotenv.config();\n\n(async () => {\n  console.log(\"process.env.HELIUS_API_KEY: \", process.env.HELIUS_API_KEY);\n\n  const HELIUS_URL = `https://rpc.helius.xyz/?api-key=${process.env.HELIUS_API_KEY}`;\n  //   // const HELIUS_URL = `https://devnet.helius-rpc.com/?api-key=${process.env.HELIUS_API_KEY}`\n  const CONNECTION = new Connection(HELIUS_URL);\n  const payer = Keypair.fromSecretKey(\n    Uint8Array.from(\n      Buffer.from(process.env.COLLECTION_OWNER_SECRET_KEY as string, \"base64\")\n    )\n  );\n  console.log(\"collection owner(payer): \", payer.publicKey.toBase58());\n\n  const balance = await CONNECTION.getBalance(payer.publicKey);\n  console.log(\"balance: \", balance);\n\n  const metaplex = Metaplex.make(CONNECTION)\n    .use(keypairIdentity(payer))\n    .use(\n      bundlrStorage({\n        address: \"https://node1.bundlr.network\",\n        providerUrl: HELIUS_URL,\n        timeout: 60000,\n      })\n    );\n\n  //STEP 1: create a collection on mainnet/devnet\n  // await createNFTCollection(payer, metaplex);\n\n  //STEP 2: create a nft on mainnet/devnet\n  // following code is to create a sample metadata to be supplied to the chatbot, sample creating nft #1 for the collection\n  const uri = uploadMetadata(metaplex, 4);\n\n  // following is the code to create a tree on the devnet, on mainnet you have to use already created trees with empty leaves\n  //   await createTree(payer, metaplex.connection, 1, 2, {\n  //     maxDepth: 5,\n  //     maxBufferSize: 8,\n  //   });\n})();\n"
  },
  {
    "path": "scripts/send.ts",
    "content": "// Deprecated - Not in use\nimport { Connection, Transaction, Keypair, Signer } from \"@solana/web3.js\";\nimport { readFileSync } from \"fs\";\n\nconst connection = new Connection(\"https://api.mainnet-beta.solana.com\");\nasync function simulate(base64Transaction: string, signers: Signer[]) {\n  const transaction = Transaction.from(\n    Buffer.from(base64Transaction, \"base64\")\n  );\n  return await connection.simulateTransaction(transaction, signers);\n}\n\nfunction parse(): {\n  keypair: Keypair;\n  base64Transaction: string;\n} {\n  let args = process.argv.slice(2);\n  if (args.length !== 4 || args[0] !== \"--tx\" || args[2] !== \"--keypair\") {\n    console.error(\n      \"Usage: ts-node scripts/send.ts --tx <base64_transaction> --keypair <path_to_keypair>\"\n    );\n    process.exit(1);\n  }\n\n  const base64Transaction = args[1];\n  const keypairPath = args[3];\n\n  const keypair = Keypair.fromSecretKey(\n    new Uint8Array(JSON.parse(readFileSync(keypairPath, \"utf-8\")))\n  );\n  return { keypair, base64Transaction };\n}\n\nasync function main() {\n  let { keypair, base64Transaction } = parse();\n  let simulationResult = await simulate(base64Transaction, [keypair]);\n  console.log(simulationResult);\n}\n\nmain();\n"
  },
  {
    "path": "scripts/uploadMetadata.ts",
    "content": "// Deprecated - Not in use\nimport {\n  Metaplex,\n  toMetaplexFile,\n  UploadMetadataInput,\n} from \"@metaplex-foundation/js\";\n\nimport * as fs from \"fs\";\n\nconst ASSETS_DIR = \"../chatgpt-plugin/public/assets/\";\nexport async function uploadMetadata(metaplex: Metaplex, nonce: number) {\n  const EXTENSION = \"png\";\n  const buffer = fs.readFileSync(`${ASSETS_DIR}${nonce}.${EXTENSION}`);\n  const file = toMetaplexFile(buffer, `${nonce}.${EXTENSION}`);\n  const imageUri = await metaplex.storage().upload(file);\n  console.log(`${ASSETS_DIR}${nonce}.json`);\n  const data = fs.readFileSync(`${ASSETS_DIR}${nonce}.json`, \"utf-8\");\n  const nftInfo = JSON.parse(data);\n\n  const nftMetadata: UploadMetadataInput = {\n    name: `${nftInfo.name ?? \"NFT\"}`,\n    symbol: `${nftInfo.symbol ?? \"NFT\"}`,\n    description: `${nftInfo.description ?? \"NFT\"}`,\n    image: imageUri,\n    properties: {\n      files: [\n        {\n          uri: `${nonce}.${EXTENSION}`,\n          type: `image/${EXTENSION}`,\n        },\n      ],\n    },\n  };\n  const { uri } = await metaplex.nfts().uploadMetadata(nftMetadata);\n  console.log(\"metadata URI\", uri);\n}\n"
  }
]