diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..647982b --- /dev/null +++ b/Dockerfile @@ -0,0 +1,30 @@ +# Use node alpine image as the base image +FROM node:alpine +# Set working directory in the container +WORKDIR /app + +# Copy package.json and package-lock.json to the working directory +COPY package*.json ./ +# Install dependencies +RUN npm install +RUN npm run build + + +# Copy the env file +COPY .env . + +# # Copy the env.txt file +# COPY env.txt ./ + +# # Create .env file and copy its content from env.txt +# RUN touch .env && \ +# cat env.txt > .env + +# Copy the rest of the application files +COPY . . + +# Expose port 3000 (assuming your application runs on this port) +EXPOSE 3000 + +# Command to run the application +CMD ["npm", "run" , "dev"] diff --git a/README.md b/README.md index e375d46..323fcbc 100644 --- a/README.md +++ b/README.md @@ -53,27 +53,61 @@ A robust and secure REST API for interacting with Ionic Protocol across multiple npm run dev ``` -## 📚 API Endpoints +## 📚 API Documentation -### Supply Assets +### Swagger UI +Access our interactive API documentation at `/api-docs` to: +- Explore all available endpoints +- Test API calls directly from your browser +- View request/response schemas +- Download OpenAPI specification + +### Available Endpoints + +
+Supply Assets + +```http POST /beta/v0/ionic/supply/:chain +``` Supply assets to an Ionic pool. +
-### Withdraw Assets +
+Withdraw Assets + +```http POST /beta/v0/ionic/withdraw/:chain +``` Withdraw your supplied assets. +
+ +
+Borrow Assets -### Borrow Assets +```http POST /beta/v0/ionic/borrow/:chain +``` Borrow assets from a pool. +
-### Repay Loan +
+Repay Loan + +```http POST /beta/v0/ionic/repay/:chain +``` Repay your borrowed assets. +
+ +
+Get Pool Address -### Get Pool Address -GET /beta/v0/ionic/pool-address/:chain/:asset -Retrieve the pool address for a specific asset. +```http +GET /beta/v0/ionic/market-address/:chain/:asset +``` +Retrieve the Market address for a specific asset. +
## 🛠️ Tech Stack @@ -96,6 +130,10 @@ This API implements several security measures: Contributions are welcome! Please feel free to submit a Pull Request. +## 📄 License + +This project is licensed under the MIT License - see the LICENSE file for details. + ## ⚡ Performance - Optimized for high throughput diff --git a/package-lock.json b/package-lock.json index ea69fa2..228de95 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,12 +13,16 @@ "dotenv": "^16.4.7", "express": "^4.18.2", "helmet": "^7.1.0", + "swagger-jsdoc": "^6.2.8", + "swagger-ui-express": "^5.0.1", "viem": "^2.0.0" }, "devDependencies": { "@types/cors": "^2.8.17", "@types/express": "^4.17.21", "@types/node": "^20.0.0", + "@types/swagger-jsdoc": "^6.0.4", + "@types/swagger-ui-express": "^4.1.7", "ts-node-dev": "^2.0.0", "typescript": "^5.0.0" } @@ -29,6 +33,50 @@ "integrity": "sha512-/3DDPKHqqIqxUULp8yP4zODUY1i+2xvVWsv8A79xGWdCAG+8sb0hRh0Rk2QyOJUnnbyPUAZYcpBuRe3nS2OIUg==", "license": "MIT" }, + "node_modules/@apidevtools/json-schema-ref-parser": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.1.2.tgz", + "integrity": "sha512-r1w81DpR+KyRWd3f+rk6TNqMgedmAxZP5v5KWlXQWlgMUUtyEJch0DKEci1SorPMiSeM8XPl7MZ3miJ60JIpQg==", + "license": "MIT", + "dependencies": { + "@jsdevtools/ono": "^7.1.3", + "@types/json-schema": "^7.0.6", + "call-me-maybe": "^1.0.1", + "js-yaml": "^4.1.0" + } + }, + "node_modules/@apidevtools/openapi-schemas": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz", + "integrity": "sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/@apidevtools/swagger-methods": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz", + "integrity": "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==", + "license": "MIT" + }, + "node_modules/@apidevtools/swagger-parser": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-10.0.3.tgz", + "integrity": "sha512-sNiLY51vZOmSPFZA5TF35KZ2HbgYklQnTSDnkghamzLb3EkNtcQnrBQEj5AOCxHpTtXpqMCRM1CrmV2rG6nw4g==", + "license": "MIT", + "dependencies": { + "@apidevtools/json-schema-ref-parser": "^9.0.6", + "@apidevtools/openapi-schemas": "^2.0.4", + "@apidevtools/swagger-methods": "^3.0.2", + "@jsdevtools/ono": "^7.1.3", + "call-me-maybe": "^1.0.1", + "z-schema": "^5.0.1" + }, + "peerDependencies": { + "openapi-types": ">=7" + } + }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -70,6 +118,12 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@jsdevtools/ono": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", + "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==", + "license": "MIT" + }, "node_modules/@noble/curves": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.7.0.tgz", @@ -109,6 +163,13 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/@scarf/scarf": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz", + "integrity": "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==", + "hasInstallScript": true, + "license": "Apache-2.0" + }, "node_modules/@scure/base": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.1.tgz", @@ -311,6 +372,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "license": "MIT" + }, "node_modules/@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", @@ -384,6 +451,24 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/swagger-jsdoc": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@types/swagger-jsdoc/-/swagger-jsdoc-6.0.4.tgz", + "integrity": "sha512-W+Xw5epcOZrF/AooUM/PccNMSAFOKWZA5dasNyMujTwsBkU74njSJBpvCCJhHAJ95XRMzQrrW844Btu0uoetwQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/swagger-ui-express": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@types/swagger-ui-express/-/swagger-ui-express-4.1.7.tgz", + "integrity": "sha512-ovLM9dNincXkzH4YwyYpll75vhzPBlWx6La89wwvYH7mHjVpf0X0K/vR/aUM7SRxmr5tt9z7E5XJcjQ46q+S3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express": "*", + "@types/serve-static": "*" + } + }, "node_modules/@types/ws": { "version": "8.5.13", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.13.tgz", @@ -474,6 +559,12 @@ "dev": true, "license": "MIT" }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", @@ -484,7 +575,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, "license": "MIT" }, "node_modules/binary-extensions": { @@ -528,7 +618,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -593,6 +682,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/call-me-maybe": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz", + "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==", + "license": "MIT" + }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -618,11 +713,19 @@ "fsevents": "~2.3.2" } }, + "node_modules/commander": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.0.tgz", + "integrity": "sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, "license": "MIT" }, "node_modules/content-disposition": { @@ -719,6 +822,18 @@ "node": ">=0.3.1" } }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/dotenv": { "version": "16.4.7", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", @@ -806,6 +921,15 @@ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", "license": "MIT" }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -920,7 +1044,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, "license": "ISC" }, "node_modules/fsevents": { @@ -1097,7 +1220,6 @@ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, "license": "ISC", "dependencies": { "once": "^1.3.0", @@ -1196,6 +1318,36 @@ "ws": "*" } }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "license": "MIT" + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "license": "MIT" + }, + "node_modules/lodash.mergewith": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", + "license": "MIT" + }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -1276,7 +1428,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -1370,12 +1521,18 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "license": "ISC", "dependencies": { "wrappy": "1" } }, + "node_modules/openapi-types": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", + "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==", + "license": "MIT", + "peer": true + }, "node_modules/ox": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/ox/-/ox-0.6.0.tgz", @@ -1418,7 +1575,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -1771,6 +1927,83 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/swagger-jsdoc": { + "version": "6.2.8", + "resolved": "https://registry.npmjs.org/swagger-jsdoc/-/swagger-jsdoc-6.2.8.tgz", + "integrity": "sha512-VPvil1+JRpmJ55CgAtn8DIcpBs0bL5L3q5bVQvF4tAW/k/9JYSj7dCpaYCAv5rufe0vcCbBRQXGvzpkWjvLklQ==", + "license": "MIT", + "dependencies": { + "commander": "6.2.0", + "doctrine": "3.0.0", + "glob": "7.1.6", + "lodash.mergewith": "^4.6.2", + "swagger-parser": "^10.0.3", + "yaml": "2.0.0-1" + }, + "bin": { + "swagger-jsdoc": "bin/swagger-jsdoc.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/swagger-jsdoc/node_modules/glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/swagger-parser": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/swagger-parser/-/swagger-parser-10.0.3.tgz", + "integrity": "sha512-nF7oMeL4KypldrQhac8RyHerJeGPD1p2xDh900GPvc+Nk7nWP6jX2FcC7WmkinMoAmoO774+AFXcWsW8gMWEIg==", + "license": "MIT", + "dependencies": { + "@apidevtools/swagger-parser": "10.0.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/swagger-ui-dist": { + "version": "5.18.2", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.18.2.tgz", + "integrity": "sha512-J+y4mCw/zXh1FOj5wGJvnAajq6XgHOyywsa9yITmwxIlJbMqITq3gYRZHaeqLVH/eV/HOPphE6NjF+nbSNC5Zw==", + "license": "Apache-2.0", + "dependencies": { + "@scarf/scarf": "=1.4.0" + } + }, + "node_modules/swagger-ui-express": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-5.0.1.tgz", + "integrity": "sha512-SrNU3RiBGTLLmFU8GIJdOdanJTl4TOmT27tt3bWWHppqYmAZ6IDuEuBvMU6nZq0zLEe6b/1rACXCgLZqO6ZfrA==", + "license": "MIT", + "dependencies": { + "swagger-ui-dist": ">=5.0.0" + }, + "engines": { + "node": ">= v0.10.32" + }, + "peerDependencies": { + "express": ">=4.0.0 || >=5.0.0-beta" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -1959,6 +2192,15 @@ "dev": true, "license": "MIT" }, + "node_modules/validator": { + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.12.0.tgz", + "integrity": "sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -2035,7 +2277,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, "license": "ISC" }, "node_modules/ws": { @@ -2069,6 +2310,15 @@ "node": ">=0.4" } }, + "node_modules/yaml": { + "version": "2.0.0-1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.0.0-1.tgz", + "integrity": "sha512-W7h5dEhywMKenDJh2iX/LABkbFnBxasD27oyXWDS/feDsxiw0dD5ncXdYXgkvAsXIY2MpW/ZKkr9IU30DBdMNQ==", + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", @@ -2078,6 +2328,36 @@ "engines": { "node": ">=6" } + }, + "node_modules/z-schema": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.5.tgz", + "integrity": "sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==", + "license": "MIT", + "dependencies": { + "lodash.get": "^4.4.2", + "lodash.isequal": "^4.5.0", + "validator": "^13.7.0" + }, + "bin": { + "z-schema": "bin/z-schema" + }, + "engines": { + "node": ">=8.0.0" + }, + "optionalDependencies": { + "commander": "^9.4.1" + } + }, + "node_modules/z-schema/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": "^12.20.0 || >=14" + } } } } diff --git a/package.json b/package.json index 0a9a72b..0b65fe2 100644 --- a/package.json +++ b/package.json @@ -14,12 +14,16 @@ "dotenv": "^16.4.7", "express": "^4.18.2", "helmet": "^7.1.0", + "swagger-jsdoc": "^6.2.8", + "swagger-ui-express": "^5.0.1", "viem": "^2.0.0" }, "devDependencies": { "@types/cors": "^2.8.17", "@types/express": "^4.17.21", "@types/node": "^20.0.0", + "@types/swagger-jsdoc": "^6.0.4", + "@types/swagger-ui-express": "^4.1.7", "ts-node-dev": "^2.0.0", "typescript": "^5.0.0" } diff --git a/src/routes/ionic/index.ts b/src/routes/ionic/index.ts index c0f3020..e6fe8bf 100644 --- a/src/routes/ionic/index.ts +++ b/src/routes/ionic/index.ts @@ -10,6 +10,7 @@ import { getChainConfig } from '../../utils/chains'; import { IonicPoolABI } from '../../abis/IonicPool'; import { createClient } from '@supabase/supabase-js'; import dotenv from 'dotenv'; +import swaggerRouter from './swagger'; dotenv.config(); @@ -53,7 +54,7 @@ async function getAssetPoolAddress( ): Promise { try { const { data, error } = await supabase - .from('asset_master_data') + .from('asset_master_data_test2') .select('ctoken_address') .eq('chain_id', getChainId(chain)) .eq('underlying_symbol', asset); @@ -245,7 +246,7 @@ router.post('/beta/v0/ionic/repay/:chain', async (req, res) => { }); // Pool address endpoint -router.get('/beta/v0/ionic/pool-address/:chain/:asset', async (req, res) => { +router.get('/beta/v0/ionic/market-address/:chain/:asset', async (req, res) => { try { const { chain, asset } = req.params; @@ -264,4 +265,5 @@ router.get('/beta/v0/ionic/pool-address/:chain/:asset', async (req, res) => { } }); +router.use(swaggerRouter); export default router; \ No newline at end of file diff --git a/src/routes/ionic/supply.txt b/src/routes/ionic/supply.txt deleted file mode 100644 index d100216..0000000 --- a/src/routes/ionic/supply.txt +++ /dev/null @@ -1,91 +0,0 @@ -import { Router } from 'express'; -import { - createPublicClient, - http, - parseEther, - encodeFunctionData, - Address -} from 'viem'; -import { getChainConfig } from '../../utils/chains'; -import { IonicPoolABI } from '../../abis/IonicPool'; - -const router = Router(); - -// Custom type for supported chains -type SupportedChain = "optimism" | "base" | "mode"; - -// Helper function to convert BigInt values to strings recursively -const serializeBigInts = (obj: any): any => { - if (typeof obj === 'bigint') { - return obj.toString(); - } - if (Array.isArray(obj)) { - return obj.map(serializeBigInts); - } - if (obj && typeof obj === 'object') { - return Object.fromEntries( - Object.entries(obj).map(([key, value]) => [key, serializeBigInts(value)]) - ); - } - return obj; -}; - -router.post('/beta/v0/ionic/supply/:chain', async (req, res) => { - try { - const { chain } = req.params; - const { - account, // User's address - amount, // Amount to supply - to // Pool address - } = req.body; - - // Type assertion for chain parameter - const chainConfig = getChainConfig(chain as SupportedChain); - - // Create public client - const publicClient = createPublicClient({ - chain: chainConfig, - transport: http() - }); - - // Encode the mint function data - const data = encodeFunctionData({ - abi: IonicPoolABI, - functionName: 'mint', - args: [parseEther(amount)] - }); - - // First simulate the transaction - const simulation = await publicClient.simulateContract({ - account: account as Address, - address: to as Address, - abi: IonicPoolABI, - functionName: 'mint', - args: [parseEther(amount)] - }); - - // Then prepare the transaction - const tx = await publicClient.prepareTransactionRequest({ - account: account as Address, - data, - to: to as Address - }); - - // Serialize and return both - const serializedTx = serializeBigInts(tx); - return res.json({ - success: true, - transaction: serializedTx, - hash: simulation.result - }); - - } catch (error: any) { - console.error('Supply error:', error); - return res.status(500).json({ - success: false, - error: error.message - }); - } -}); - -export default router; \ No newline at end of file diff --git a/src/routes/ionic/swagger.ts b/src/routes/ionic/swagger.ts new file mode 100644 index 0000000..78ffe59 --- /dev/null +++ b/src/routes/ionic/swagger.ts @@ -0,0 +1,294 @@ +import { Router } from 'express'; +import swaggerJsdoc from 'swagger-jsdoc'; +import swaggerUi from 'swagger-ui-express'; + +const options = { + definition: { + openapi: '3.0.0', + info: { + title: 'Ionic API', + version: '0.1.0', + description: 'API for interacting with Ionic Protocol pools', + }, + components: { + schemas: { + TransactionRequest: { + type: 'object', + properties: { + success: { type: 'boolean' }, + transactionRequest: { + type: 'object', + properties: { + to: { type: 'string' }, + data: { type: 'string' }, + from: { type: 'string' }, + value: { type: 'string', nullable: true }, + }, + }, + }, + }, + Error: { + type: 'object', + properties: { + success: { type: 'boolean' }, + error: { type: 'string' }, + }, + }, + }, + }, + paths: { + '/beta/v0/ionic/supply/{chain}': { + post: { + tags: ['Ionic Operations'], + summary: 'Supply assets to an Ionic pool', + parameters: [ + { + name: 'chain', + in: 'path', + required: true, + schema: { + type: 'string', + enum: ['optimism', 'base', 'mode', 'bob', 'fraxtal', 'lisk', 'ink', 'superseed', 'worldchain', 'swell', 'soneium', 'ozeantest', 'camptest'], + }, + }, + ], + requestBody: { + required: true, + content: { + 'application/json': { + schema: { + type: 'object', + required: ['account', 'amount', 'asset'], + properties: { + account: { type: 'string' }, + amount: { type: 'string' }, + asset: { type: 'string' }, + }, + }, + }, + }, + }, + responses: { + 200: { + description: 'Successful operation', + content: { + 'application/json': { + schema: { $ref: '#/components/schemas/TransactionRequest' }, + }, + }, + }, + 400: { + description: 'Invalid input', + content: { + 'application/json': { + schema: { $ref: '#/components/schemas/Error' }, + }, + }, + }, + }, + }, + }, + '/beta/v0/ionic/withdraw/{chain}': { + post: { + tags: ['Ionic Operations'], + summary: 'Withdraw assets from an Ionic pool', + parameters: [ + { + name: 'chain', + in: 'path', + required: true, + schema: { + type: 'string', + enum: ['optimism', 'base', 'mode', 'bob', 'fraxtal', 'lisk', 'ink', 'superseed', 'worldchain', 'swell', 'soneium', 'ozeantest', 'camptest'], + }, + }, + ], + requestBody: { + required: true, + content: { + 'application/json': { + schema: { + type: 'object', + required: ['account', 'amount', 'asset'], + properties: { + account: { type: 'string' }, + amount: { type: 'string' }, + asset: { type: 'string' }, + }, + }, + }, + }, + }, + responses: { + 200: { + description: 'Successful operation', + content: { + 'application/json': { + schema: { $ref: '#/components/schemas/TransactionRequest' }, + }, + }, + }, + 400: { + description: 'Invalid input', + content: { + 'application/json': { + schema: { $ref: '#/components/schemas/Error' }, + }, + }, + }, + }, + }, + }, + '/beta/v0/ionic/borrow/{chain}': { + post: { + tags: ['Ionic Operations'], + summary: 'Borrow assets from an Ionic pool', + parameters: [ + { + name: 'chain', + in: 'path', + required: true, + schema: { + type: 'string', + enum: ['optimism', 'base', 'mode', 'bob', 'fraxtal', 'lisk', 'ink', 'superseed', 'worldchain', 'swell', 'soneium', 'ozeantest', 'camptest'], + }, + }, + ], + requestBody: { + required: true, + content: { + 'application/json': { + schema: { + type: 'object', + required: ['account', 'amount', 'asset'], + properties: { + account: { type: 'string' }, + amount: { type: 'string' }, + asset: { type: 'string' }, + }, + }, + }, + }, + }, + responses: { + 200: { + description: 'Successful operation', + content: { + 'application/json': { + schema: { $ref: '#/components/schemas/TransactionRequest' }, + }, + }, + }, + 400: { + description: 'Invalid input', + content: { + 'application/json': { + schema: { $ref: '#/components/schemas/Error' }, + }, + }, + }, + }, + }, + }, + '/beta/v0/ionic/repay/{chain}': { + post: { + tags: ['Ionic Operations'], + summary: 'Repay borrowed assets to an Ionic pool', + parameters: [ + { + name: 'chain', + in: 'path', + required: true, + schema: { + type: 'string', + enum: ['optimism', 'base', 'mode', 'bob', 'fraxtal', 'lisk', 'ink', 'superseed', 'worldchain', 'swell', 'soneium', 'ozeantest', 'camptest'], + }, + }, + ], + requestBody: { + required: true, + content: { + 'application/json': { + schema: { + type: 'object', + required: ['account', 'amount', 'asset'], + properties: { + account: { type: 'string' }, + amount: { type: 'string' }, + asset: { type: 'string' }, + }, + }, + }, + }, + }, + responses: { + 200: { + description: 'Successful operation', + content: { + 'application/json': { + schema: { $ref: '#/components/schemas/TransactionRequest' }, + }, + }, + }, + 400: { + description: 'Invalid input', + content: { + 'application/json': { + schema: { $ref: '#/components/schemas/Error' }, + }, + }, + }, + }, + }, + }, + '/beta/v0/ionic/market-address/{chain}/{asset}': { + get: { + tags: ['Pool Information'], + summary: 'Get pool address for a specific asset', + parameters: [ + { + name: 'chain', + in: 'path', + required: true, + schema: { + type: 'string', + enum: ['optimism', 'base', 'mode', 'bob', 'fraxtal', 'lisk', 'ink', 'superseed', 'worldchain', 'swell', 'soneium', 'ozeantest', 'camptest'], + }, + }, + { + name: 'asset', + in: 'path', + required: true, + schema: { type: 'string' }, + }, + ], + responses: { + 200: { + description: 'Successful operation', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + success: { type: 'boolean' }, + poolAddress: { type: 'string' }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + apis: ['./src/routes/ionic/index.ts'], // Path to the API routes +}; + +const specs = swaggerJsdoc(options); + +const router = Router(); +router.use('/api-docs', swaggerUi.serve); +router.get('/api-docs', swaggerUi.setup(specs)); + +export default router; \ No newline at end of file